分布式事务
为什么跨数据库提交比单库事务更难?
- 分布式事务: 一个业务动作修改两个或更多数据库,需要协调各参与者的提交结果;
- 核心困难: 消息可丢失、参与者可崩溃,某节点看见超时却不知道别人是否已经提交;
- 目标区分: 原子提交力求所有参与者作出一致提交决定,业务补偿则接受分步提交后修复,二者不是同一保证;
两阶段提交怎样形成一致决定?
协调者 → Prepare → 参与者持久记录准备状态并投票
全部同意 → 持久记录 Commit 决定 → 通知参与者提交
无法全部准备 → 持久记录 Abort 决定 → 通知参与者撤销
- 准备阶段: 参与者确认自己有能力提交并保留必要资源,准备成功不是已经对外提交;
- 提交阶段: 协调者依据投票作出并持久化全局决定,参与者按该决定完成;
- 阻塞风险: 已准备的参与者在协调者失联后可能无法自行判断最终决定,导致资源长时间被占用;
- 原文修正: 一旦全局提交决定已持久化,后续故障通常应恢复并继续提交,不能任意把已提交事务回滚;
- 适用条件: 参与者支持协议且业务确需原子提交,能够承担协调与可用性代价;
三阶段提交怎样缩小不确定状态?
CanCommit / Prepare → PreCommit → DoCommit
- PreCommit: 增加一个明确的中间阶段,让参与者知道所有成员已经越过准备检查;
- 收益: 在特定同步、超时和故障假设下,可以降低两阶段提交的阻塞问题;
- 限制: 不能在任意网络分区和无限消息延迟下仅靠超时同时保证原子性与不停机;
- 设计判断: 必须说明时间与故障假设,不能把“多一阶段”当作普适的高可用解法;
Saga 如何补偿已经完成的本地事务?
- Saga: 把长业务流程拆成若干本地事务,失败时执行相应补偿动作来恢复业务可接受状态;
- 示例: 预订酒店成功而订票失败后发起取消酒店,取消是新的业务事务,不是抹去已发生的历史;
- 隔离代价: 中间状态可能被其他操作观察到,需要预留、状态校验或其他业务规则控制冲突;
- 补偿失败: 补偿本身也可能失败,应允许重试、记录进度并提供人工修复入口;
Saga 的下一步由谁驱动?
| 方式 | 驱动方法 | 权衡 |
|---|---|---|
| Choreography 编舞 | 各服务订阅事件并触发后续动作 | 自主性高,但整体流程和循环依赖难看清 |
| Orchestration 编排 | 编排器记录状态并指挥参与者 | 流程明确,但需维护编排器可靠性 |
- 运维难点: 调试、追踪、重放与测试需要覆盖跨服务失败组合,不能只测每个本地事务成功;