服务拆分
2026/10/11大约 5 分钟
服务拆分
微服务最难的不是「怎么通信」,而是「切在哪里」。边界切对了,治理和事务都是顺水推舟;切错了,就是在给自己造「分布式单体」。
1. 拆分的判断依据
服务边界应当高内聚、低耦合:一个服务内部的东西紧密相关,服务之间的依赖尽量少。常用几种视角:
| 视角 | 依据 | 说明 |
|---|---|---|
| 业务能力 | 业务做什么 | 按「订单、支付、库存、用户」这类业务能力划分,最直观 |
| 限界上下文(DDD) | 领域模型边界 | 同一术语在不同上下文含义不同(如「商品」在销售 vs 库存里不一样),边界即上下文边界 |
| 变更频率 | 谁经常一起改 | 频繁一起变更的东西应放在一起;变更节奏差异大的应拆开 |
| 扩展特性 | 谁需要独立扩缩容 | 读多写少、流量尖峰的服务独立出去 |
| 组织架构(康威定律) | 团队划分 | 康威定律:系统结构会趋同于组织的沟通结构。团队边界常是天然的服务边界 |
DDD 的核心贡献
限界上下文(Bounded Context) 是服务拆分最有价值的方法:它强调「边界内术语含义统一、边界外无需统一」,从根本上解释了为什么不该按技术分层(Controller/Service/DAO)拆分,而应按业务语义拆分。
2. 数据归属是硬约束
拆服务的本质是拆数据。一个服务应该独占自己的数据,「谁的数据谁负责」。这是判断拆分是否成立的关键标准:
- 如果两个服务必须共享同一张表的写操作,说明它们可能不该拆,或者需要重新划分数据归属。
- 跨服务共享数据库是典型反模式——它让服务之间通过表结构隐式耦合,任何一方改表都影响另一方,独立性荡然无存。
- 跨服务的数据一致性,靠 分布式事务 或最终一致(事件 + 补偿)解决。
3. 拆分步骤
一个可操作的流程:
- 画出当前模块依赖图:先看清单体里「谁调用谁、谁改哪些表」。
- 识别数据归属与事务边界:哪些数据必须一起原子更新?这往往就是不可分割的最小边界。
- 从边缘、读多写少的模块先拆:风险低、见效快,例如通知、报表、搜索。
- 定义服务间契约:同步用 API(REST/RPC),异步用事件;明确输入输出与幂等。
- 观察稳定后再动核心交易:核心域事务重、风险高,最后拆,且要保留回滚路径。
拆的过程中常配合 绞杀者模式(Strangler Fig):在旧系统外面套一层门面,把功能一块块迁移到新服务,旧代码逐步「被绞杀」直至下线——比「推倒重来」风险低得多。
4. 拆分策略:按什么切
| 策略 | 做法 | 适用 |
|---|---|---|
| 按业务能力 | 每个业务能力一个服务 | 通用首选 |
| 按限界上下文 | 每个上下文一个服务或一组服务 | 领域复杂的系统 |
| 按读写分离(CQRS) | 写模型与读模型分开 | 读远多于写 |
| 按可变/不可变 | 稳定数据与高频变更数据分离 | 有明显冷热之分 |
| 绞杀者渐进迁移 | 逐步从单体剥离 | 既有系统改造 |
5. 反模式(一定要避开)
| 反模式 | 表现 | 后果 |
|---|---|---|
| 共享大数据库 | 所有服务连同一个库 | 隐式耦合,改表即灾难 |
| 分布式大单体 | 拆了进程但服务间强耦合、必须一起发布 | 复杂度上升、收益为零 |
| 聊天式调用 | A→B→C→D 一串同步调用 | 延迟累加、故障放大、链路难维护 |
| 循环依赖 | A 调 B、B 调 A | 无法独立部署、发布死锁 |
| 服务过细 | 一个功能拆成十几个纳米服务 | 运维与调用开销爆炸 |
| 共享库耦合 | 大量服务依赖同一个「公共逻辑包」 | 改一次库全量重新发布 |
同步调用链过深是隐形杀手
每多一跳同步调用就多一份延迟和一次失败可能。若调用链有 4 层、每层成功率 99.9%,端到端成功率只有约 99.6%,且尾延迟被放大。能用事件异步解耦的,就别串成同步长链。
6. 拆分粒度怎么把握
「一个服务多大合适」没有标准答案,但有几条经验:
- 一个团队能完整拥有它:从开发到运维,一个小组(通常 5–9 人)能独立负责。
- 能独立部署:改动不需要和其他服务协调发布。
- 职责单一但不过碎:以「一个业务能力」为单元,而不是「一个接口」。
- 拆不动就先不拆:先模块化,边界稳定后再物理拆分。逻辑边界清楚后,物理拆分只是加个进程。
7. 一个拆分示例
以电商系统为例,按业务能力与限界上下文可以先切出:
| 服务 | 核心职责 | 数据归属 |
|---|---|---|
| 用户服务 | 注册、登录、资料 | user_db |
| 商品服务 | 商品、类目、上下架 | product_db |
| 订单服务 | 下单、状态流转 | order_db |
| 库存服务 | 库存扣减、锁定 | stock_db |
| 支付服务 | 支付、退款、对账 | pay_db |
拆分顺序建议:先拆用户、商品这类读多写少、边界清晰的服务;订单与库存涉及跨服务事务,放在后面,并用本地消息表或最终一致承接(见 分布式事务)。切忌一开始就按「一张表一个服务」去切。
8. 小结
- 拆分的核心是边界,目标是高内聚、低耦合。
- 限界上下文(DDD) 与 业务能力 是最可靠的划分依据,康威定律 说明组织边界即服务边界。
- 数据归属是硬约束:一个服务独占自己的数据,不共享库。
- 用绞杀者模式渐进迁移,先拆边缘与读多写少的模块。
- 警惕共享大库、分布式大单体、聊天式长调用链等反模式。