16-业务系统边界与架构取舍
业务系统怎样划边界?模块、权限、审批与微服务取舍 把订单、审批和用户拆成三个服务,不会自动让系统更清楚。数据权限怎么贯穿查询,审批完成怎么与业务状态一致,拆分后事务怎么恢复,才是决定架构是否可维护的问题。 1. 先确定业务能力与拆分理由 先建立边界,再讨论进程数量与通信方式。 单体和微服务的差别不只
业务系统怎样划边界?模块、权限、审批与微服务取舍 把订单、审批和用户拆成三个服务,不会自动让系统更清楚。数据权限怎么贯穿查询,审批完成怎么与业务状态一致,拆分后事务怎么恢复,才是决定架构是否可维护的问题。 1. 先确定业务能力与拆分理由 先建立边界,再讨论进程数量与通信方式。 单体和微服务的差别不只
消息为什么会丢、会重?可靠投递、消费幂等与 Outbox 订单已经提交,消息没有发出;消息已经处理,确认却丢了。这两个窗口分别造成遗漏与重复。可靠消息设计要覆盖整条链路,而不是只打开 Broker 的一个开关。 1. 三段链路需要不同确认 生产、路由、持久保存和消费提交不是一个成功信号。 三段链路各
分布式锁到底保护了什么?租约、过期执行者与业务约束 进程拿到锁之后暂停很久,锁到期被其他进程获得,旧进程恢复后仍继续写数据。这个场景说明锁可以协调进入资格,却未必能阻止过期执行者产生副作用。 1. 等待时间、租约与续期分别控制什么 把锁获取与锁持有时间分开,再理解 watchdog。 等待时间与租约
Redis 缓存怎样保持可靠?失效、回填与一致性窗口 缓存失效时大量请求击中数据库,更新后又偶尔读到旧数据,看起来是两个故障,其实都与缓存的生命周期有关。先确认权威数据在哪里,再决定读、写、失效和恢复怎样协作。 1. 先分清未命中为什么发生 穿透、热点失效和大范围失效需要不同处理。 三种问题的差别
事务之间怎样相处?MVCC、锁等待与死锁恢复 同一个事务里读取可能看到旧值,更新却作用于新值;没有查到记录,也可能阻止其他会话插入。理解这些现象,需要把版本读取与锁定访问分开,不能只背可重复读的定义。 具体 SQL 观察沿用原有 MySQL 8.0.29 示例。不同实验先执行自己的数据准备,执行计划
慢 SQL 怎样优化?从查询语义到执行计划证据 EXPLAIN 显示使用索引,接口依然很慢;增加一个索引后,排序好了,返回大字段又变慢。索引只是访问路径的一部分,优化要回答究竟减少了哪些扫描、回表、排序或等待。 具体 SQL 观察沿用原有 MySQL 8.0.29 示例。不同实验先执行自己的数据准备
Mapper 背后发生了什么?MyBatis 执行链与更新一致性 Mapper 只有接口,没有手写实现,SQL 却可以执行;同一查询调用两次,又不一定发送两次 SQL。理解代理、会话和执行器之后,分页、乐观锁及逻辑删除才能被放进正确的业务边界。 涉及 Spring 的示例沿用 Java 17、Spr
Transactional 为什么没生效?代理、异常与事务边界 方法已经加了 Transactional,失败后数据却没有回滚。最值得先问的是:调用是否经过代理,异常是否穿过事务边界,以及几次写入是否使用同一个事务资源。注解只能参与机制,不能替代这些条件。 涉及 Spring 的示例沿用 Java
Spring Boot 为什么能自动装配?Bean 生命周期与条件配置 加入 starter 后接口就能调用,自己定义一个 Bean 后默认实现又消失了。这不是依赖下载时发生了魔法,而是容器创建、配置发现和条件判断共同决定最终对象。理解这些机制,也能解释升级后为何选中了不同实现。 涉及 Spring
一次 HTTP 请求怎样穿过 Spring MVC?从入口到 JSON 响应 Controller 没有进入,返回值却能变成 JSON,参数无效时又是谁提前拦下请求?沿着一次请求逐层观察,比记住一串组件名称更容易理解 Spring MVC,也能更快定位 404、400 和异常响应。 涉及 Sprin