← 返回题目列表

设计分布式系统时如何拆业务边界和 SLA?

高频 中等 第 7 / 27 题 更新于 2026/09/30
服务边界SLASLO依赖治理
学习 AI 实战项目

简化版

业务边界应围绕业务能力、数据所有权和一致性不变量拆分,而不是按数据库表或技术分层机械切服务。SLA 则要从用户旅程反推到各服务的 SLO 和依赖预算,明确可用性、延迟、数据正确性、恢复目标及降级方式;边界与 SLA 必须一起设计,否则一次用户请求跨越过多服务后,整体可靠性会迅速下降。

详细版

拆边界时先识别业务动作和不变量:哪些数据必须在一个原子边界内修改,谁是权威写入者,哪些信息可以通过事件异步传播。高内聚、低耦合不是让服务越小越好,而是让一个业务能力的规则和数据归同一所有者,跨边界只暴露稳定契约。

SLA 是对外承诺,内部通常用 SLO 管理目标,用 SLI 测量结果。至少定义成功率、P95/P99 延迟、数据新鲜度、RTO/RPO;再把端到端目标分配给关键依赖,并保留误差预算。若同步链路串联 10 个各为 99.9% 可用的服务,粗略整体成功率只有 0.999^10 ≈ 99.0%,说明边界过碎或缺少降级。

落地时要同时画业务边界、数据边界和故障边界。核心路径减少同步依赖,非核心能力异步化或可降级;跨服务更新通过状态机、可靠事件和补偿处理。任何 SLA 都要注明统计窗口、排除项和测量位置,不能只写一个“99.99%”。

完整版教学

一、服务边界首先是业务边界

服务拆分的目标是让业务规则有清晰归属。例如订单服务负责订单生命周期,库存服务负责可售数量,支付服务负责支付交易;它们不是因为各有一张表才成为服务,而是因为拥有不同的不变量和演进节奏。

若按 Controller、Service、DAO 技术层拆成三个远程服务,一次简单调用会产生多次网络往返,却没有获得业务自治。相反,把所有业务塞进一个“大中台”又会让发布和故障耦合。合理粒度应让大多数规则在边界内完成,跨边界协作显式化。

记忆钩子:先问“谁对这条业务规则和这份数据负责”,再问“要不要成为独立服务”。

二、用不变量确定数据边界

不变量是任何时刻都不能被破坏的条件,如余额不能凭空增加、库存不能为负、同一支付单只能成功一次。需要强原子约束的数据优先放在同一事务边界内;若拆到多个服务,就必须接受协调、预留或补偿的复杂度。

订单创建:订单号唯一、状态迁移合法 -> 订单边界
库存扣减:available >= 0          -> 库存边界
支付入账:借贷守恒、流水唯一       -> 支付边界
跨边界流程:订单待支付 -> 支付结果事件 -> 订单确认

数据应有单一权威写入者。其他服务需要信息时通过 API 查询、订阅事件建立只读副本,不能随意直写对方表,否则规则会散落,版本升级也无法控制。

三、SLA、SLO、SLI 分别是什么

SLI 是实际测量指标,例如“有效订单请求中成功请求占比”;SLO 是内部目标,例如 30 天窗口达到 99.95%;SLA 是对客户的承诺及未达标责任。三者口径必须一致,但 SLA 往往比内部 SLO 稍宽,以保留运营空间。

项目示例容易漏掉的口径
可用性30 天成功率 ≥ 99.95%哪些状态码算失败、客户端取消是否计入
延迟P99 ≤ 300ms从网关还是服务端开始测量
新鲜度99% 索引在 60s 内更新以事件产生还是入队时间起算
RTO故障后 15 分钟恢复恢复到何种业务能力
RPO最多丢失 1 分钟数据哪个事实源及哪些操作

只有数字没有窗口和测量位置,就无法判断是否违约,也无法指导架构。

四、端到端可靠性为什么会被依赖放大

串行依赖全部成功时,整体成功率近似为各段成功率乘积。5 个独立依赖各为 99.9%,整体约为 0.999^5 = 99.50%;20 个则约为 98.02%。真实故障往往相关,结果可能更差。

延迟也会累加。三个串行调用 P99 各为 100ms,端到端 P99 不能简单保证在 100ms 内;若存在扇出,最慢分支决定返回时间。因此核心链路要减少同步跳数,将通知、报表等非关键动作移出主路径,并对可选依赖准备降级。

用户请求 -> 网关 -> 订单 -> 库存 -> 支付
                     \-> 推荐(可降级,不应阻断订单)

五、怎样把端到端目标分解

目标分配不是把 300ms 平均分给三个服务。要先测量不可控成本,如公网 40ms、数据库 P99 80ms,再给业务计算和排队留预算。调用方的子调用超时还必须小于自身剩余 deadline,以便留出序列化和返回时间。

假设端到端预算 500ms,网关与网络预留 80ms,订单本地处理 70ms,库存 120ms,支付 180ms,再留 50ms 安全余量。若支付固定配置 500ms 超时,订单根本没有机会在总预算内返回,说明局部参数没有服从端到端目标。

错误预算同样可分解。99.95% 月度 SLO 在 30 天内允许约 21.6 分钟不可用;预算消耗过快时应冻结高风险发布或加强保护,而不是等月底计算报表。

六、边界要同时成为故障隔离边界

独立服务若共享同一线程池、数据库账号或消息队列拥塞点,逻辑上拆开也不能隔离故障。应按业务优先级划分连接池、队列、限流配额和部署单元,避免批量任务拖垮在线请求。

隔离也不是无限复制资源。为 20 个小服务各建独立数据库会增加运维和成本;可以共享物理集群但分配逻辑配额、独立账号和熔断策略。关键是故障影响范围可预测,并能在指标中区分。

当依赖不可用时,要预先定义是失败、返回旧数据、进入处理中还是异步补偿。降级结果不能违反核心不变量。

七、如何验证拆分是否合理

结构指标可以辅助判断:一次业务请求跨越多少同步服务,一个变更需同时发布多少仓库,一份数据有几个写入者,一个服务故障影响多少业务。若“修改优惠规则”总要联动 8 个服务发布,边界可能把一个业务能力切碎了。

容量演练要覆盖依赖慢、超时和局部不可用,而非只测试正常吞吐。监控端到端 SLI,并能下钻到各依赖预算消耗。分布式追踪用于定位,不应成为计算唯一业务成功率的来源,因为采样可能漏掉请求。

journey: create-order
slo_success_30d: 99.95%
slo_p99_ms: 500
sync_dependency_count: 3
max_stale_seconds: 0
degradable_dependencies: [recommendation]

八、常见误区与追问

  • 误区:微服务越小越符合单一职责。 过细边界会增加网络失败、跨服务事务和协同发布,职责应按业务能力而非类数量判断。
  • 误区:每个服务 99.9%,整条链路也是 99.9%。 串行依赖成功率会相乘,整体目标需要更高的局部目标或减少依赖。
  • 误区:SLA 只写可用性百分比即可。 还要定义统计窗口、请求口径、延迟、数据正确性和恢复目标。
  • 误区:数据库独立就完成了服务自治。 若多个服务仍互相写表或必须同步发布,所有权并未真正分开。
  • 追问:服务边界拆错了怎么办? 用兼容接口和事件逐步迁移所有权,先阻止新增耦合,再双写/回填并切换,避免一次性大改。
  • 追问:共享数据库一定不允许吗? 可共享物理设施,但应保持 schema、账号和写入所有权隔离,避免绕过服务契约。
  • 追问:错误预算有什么用? 它把可靠性与发布速度连接起来,预算充足可迭代,消耗过快则优先稳定性工作。

九、加强记忆

把边界和 SLA 记成“规则归属、数据所有、链路预算、故障隔离”。先用业务不变量决定哪些动作必须留在一起,再指定数据唯一写入者;随后从用户旅程反推成功率和延迟预算,最后验证服务边界是否也隔离资源与故障。拆分不是画更多方框,而是让责任、契约和可靠性都能独立演进。