Day31-20|10.6.9 大型网站系统架构演化实例(九):业务拆分

一、教材定位

业务拆分是大型网站从粗粒度应用走向更细粒度服务和领域架构的重要阶段。前面的垂直架构已经按业务功能形成了较大应用,随着团队、用户和需求继续增长,单个业务应用内部仍可能发布阻塞、职责混杂和故障相互影响。业务拆分要进一步围绕业务能力、数据所有权、变化频率和质量属性建立更清晰的边界。

业务拆分不是把代码目录机械地切成多个服务,也不是服务数量越多越先进。拆分后会引入远程调用、分布式事务、消息、版本、部署、监控和团队协作成本。合理的拆分应减少不必要的耦合,支持独立变化和故障隔离,同时保留能够被验证和运维的整体业务能力。

二、为什么需要业务拆分

1. 业务变化频率不同

促销规则、推荐算法和内容展示可能变化很快,订单状态和账务规则变化较慢但可靠性要求高。把它们放在同一应用中,会让低风险变化受到核心流程发布约束,也会让高风险模块被频繁修改。

2. 资源和质量目标不同

搜索、推荐和内容浏览偏向高吞吐和低延迟,支付和账务偏向一致性、审计和安全,后台报表偏向批量处理。拆分可以为不同业务选择适合的资源、部署和数据策略。

3. 团队和责任边界需要清晰

当多个团队共同修改同一代码和数据库时,沟通成本、合并冲突和发布协调不断增加。业务拆分可以让团队围绕能力负责开发、上线、监控和故障恢复,但前提是边界真实且团队具备独立能力。

4. 故障隔离和弹性需求

非核心业务故障不应拖垮核心交易;热点业务需要独立扩容;不同区域和租户可能需要不同故障域。业务拆分可以缩小故障半径和扩展范围。

三、业务边界的识别方法

1. 业务能力

从系统必须提供的能力出发,例如用户身份、商品管理、订单、支付、库存、物流、营销、搜索和内容。能力应有明确目标、输入、输出和责任人。

2. 领域事件

观察业务中发生的事实和状态变化,如订单已创建、支付已确认、库存已预占和商品已发布。事件可以帮助识别生产者、消费者和数据所有权,但不能仅凭事件名称决定服务边界。

3. 数据所有权

谁负责创建、修改、校验、保留和审计某类数据,通常是重要边界信号。一个服务若只是读取他人数据,不应直接拥有写权限;多个团队共同修改同一实体,可能说明边界尚未理清。

4. 变化原因

经常一起变化的规则和数据可以放在一起,不同原因变化的内容应谨慎分开。按技术层拆分通常不能降低业务耦合,按变化原因和业务职责拆分更有价值。

5. 事务和不变量

必须原子维护的不变量应尽量放在同一边界内。例如扣减库存和确认预占可能属于同一库存聚合;支付扣款和账务入账需要严格设计事务和一致性。若跨边界才可完成,必须明确分布式协作和补偿。

四、拆分粒度的权衡

1. 过粗

一个服务包含大量变化不同的能力,仍然存在发布阻塞、资源竞争和故障传播。边界只是名称变化,实际耦合没有减少。

2. 过细

服务数量过多,调用链、部署、监控、权限和数据一致性成本大幅增加。小服务之间频繁同步调用,系统可能从单体内部耦合变成分布式网络耦合。

3. 合适粒度

服务应有清晰业务目标、相对独立的数据和变化原因、可理解的接口、独立的质量目标和可承担的运维责任。粒度不是一次决定的,可以通过运行数据和故障反馈持续调整。

五、业务拆分后的协作方式

1. 同步接口

适合需要即时结果、失败可立即反馈和查询范围明确的场景。要设计超时、重试、熔断、限流、认证和降级,避免同步链路过长。

2. 异步事件

适合通知、索引、积分、报表和非核心联动。要处理消息可靠性、重复、乱序、积压、死信和回放。事件消费者不能直接假设消息只到达一次。

3. 查询模型和数据副本

为减少跨服务实时查询,可以通过事件构建读模型、缓存或搜索索引。派生数据允许延迟,但要有版本、重建、对账和过期策略。

4. 组合和编排

跨服务流程可以由编排器协调,也可以由事件驱动形成编舞。编排器便于表达顺序、超时和补偿,编舞解耦参与者但更难理解和追踪。核心流程应选择可观察、可恢复的方式。

六、分布式事务与一致性

业务拆分后,原来数据库事务覆盖的操作可能跨越多个服务。应先判断是否真的需要强一致,能否重新划分边界;不能简单把本地事务复制为多个远程事务。

常见方式包括可靠消息、Outbox、Saga、TCC、状态机和业务补偿。选择时要考虑业务副作用、回滚是否可逆、超时、人工处理和对账。最终一致性必须有收敛目标和异常处理,而不是“稍后会一致”的模糊承诺。

七、迁移实施步骤

1. 建立现状和边界地图

梳理模块、调用、表、字段、消息、批任务、权限、团队和故障。识别最频繁变化、最影响交付和最有价值的拆分点。

2. 先封装再迁移

在旧系统内部建立接口和领域边界,禁止新增跨模块直接访问。这样可以先降低耦合,再把实现迁移到独立服务。

3. 选择先行能力

优先选择边界清晰、影响可控、可独立验证的能力,如搜索、通知、内容或营销,而不是一开始拆分复杂核心交易。

4. 兼容和灰度

使用适配器、接口版本、双读、影子调用和按租户切换,保持新旧逻辑并行。新服务先接收少量流量,比较结果和资源,再逐步扩大。

5. 数据迁移和权限收缩

明确权威来源,回填和对账,迁移写入后逐步收回旧数据库权限。旧代码、脚本和报表必须同步处理。

6. 退出旧结构

确认旧调用、消息、定时任务和权限没有使用,删除适配层和双写逻辑,更新架构定义和运维手册。

八、案例分析:电商业务拆分

某网站的交易应用包含订单、库存、支付、营销和通知。订单创建时同步调用所有模块并共享数据库,促销规则变化频繁,通知失败却会阻塞订单,库存高峰时影响支付。

拆分前先定义边界:订单负责订单生命周期,库存负责可售和预占,支付负责支付流水和回调,营销负责优惠计算,通知负责非核心消息。订单和库存的核心不变量通过明确接口和状态机保护,营销先以查询和策略服务方式隔离,通知改为可靠事件。支付回调使用幂等键和状态转换,通知失败不回滚已确认的支付。

迁移采用旧订单流程和新编排流程并行,通过影子计算比较优惠结果,逐步切换营销流量;库存先封装数据访问,再迁移写入并执行库存对账;通知使用消息队列和死信。监控跨服务延迟、消息积压、订单成功率、库存差异、重复支付和补偿时长。完成后收回共享数据库权限,删除旧调用,确认各团队具备独立发布和值班能力。

九、综合知识练习

题1

业务拆分最重要的依据是什么?

参考答案:业务能力、数据所有权、变化原因、事务和不变量、质量属性以及团队责任,而不是代码行数或服务数量。边界应能够减少不必要耦合并支持独立变化。

题2

业务拆分后如何处理原来的本地事务?

参考答案:先判断是否需要强一致并尽量重新划分边界;跨边界流程采用可靠消息、状态机、Saga、TCC、幂等、补偿和对账,明确部分成功、超时、重试和人工接管。

题3

为什么通知等非核心业务适合先拆分?

参考答案:通常边界清晰、与核心交易一致性要求较低,可以异步处理,拆分后影响范围可控,容易通过消息、重试和死信验证,为后续复杂拆分积累经验。

十、论文写作素材

论文中可以这样描述业务拆分:

“针对交易应用中订单、库存、支付、营销和通知职责混杂、发布相互阻塞的问题,我依据业务能力、数据所有权、变化原因和不变量重新划分边界。订单负责生命周期,库存负责预占与释放,支付负责流水和回调,营销负责规则计算,通知作为非核心事件消费者。迁移先在单体内部封装接口和禁止跨模块直接访问,再通过适配器、影子执行、灰度、可靠消息和业务对账逐步切流。跨服务流程使用状态机、幂等、补偿和死信处理,完成后收回共享数据库权限并通过发布周期、故障影响、数据差异和团队独立交付能力验证效果。”

十一、今日复盘

  1. 业务拆分是按业务能力、数据所有权和变化原因建立边界。
  2. 拆分粒度过粗会保留耦合,过细会放大分布式复杂度。
  3. 同步接口、异步事件、查询模型和流程编排要按场景选择。
  4. 分布式事务需要不变量、幂等、补偿、对账和人工接管。
  5. 迁移应先封装、再兼容、后灰度,最后收回旧权限并退出旧结构。
  6. 案例答题要体现业务边界、数据权威、迁移路径和质量验证。