初识分布式事务原理
事务是指符合ACID特性的操作就是事务,在同一个数据库中,如果要分别对表A和表B进行插入和删除操作,如果其中一个操作执行失败,可以对当前数据库进行回滚,使其回滚到执行操作前的状态,但是现有的系统架构都是基于微服务的,不同服务和操作设计不同的数据库和表,如果插入操作使用的是数据库A,删除操作使用的是数据库B,那么如果想要二者符合事务的特性,要么全部执行成功,要么全部执行失败,如果失败就要进行回滚,但是不同的数据库又不能同时回滚,因此就需要使用分布式事务。
分布式事务 是指:一个业务操作需要跨多个独立的系统(服务、数据库、消息队列等)完成,而我们仍然希望这个操作具有像本地事务一样的特性 —— 要么全部成功,要么全部失败(原子性)。
常见的分布式事务解决方案有:
1. 2PC(Two Phase Commit)(两阶段提交)
2PC是一种最传统的分布式事务协议,用于确保多个数据库、系统之间在事务上达成“一致性或回滚”,它是很多数据库和中间件(如XA和JTA)支持的底层协议。
工作流程涉及一个协调者+多个参与者
1️⃣ 预提交阶段(Prepare Phase)
- 协调者向所有参与者发送“准备提交事务吗?”请求。
- 每个参与者执行本地操作,但不提交,锁住资源。
- 每个参与者回复“准备好了(Yes)”或“失败(No)”。
2️⃣ 提交阶段(Commit Phase)
- 如果所有参与者都回复 Yes,协调者广播 “正式提交”;
→ 所有参与者提交事务,释放资源。
- 如果有任一 No,协调者广播 “回滚”;
→ 所有参与者回滚之前的操作。
两阶段提交通过先让所有参与者先在本地进行执行,但不提交,确保所有参与者执行成功再提交,因此其一致性强,适用于对一致性要求极高的常见,但是缺点是所有参与者在预提交阶段不会进行提交而是锁住资源,会阻塞数据库,并且如果协调者发生“宕机”,会导致所有参与者卡死在等待阶段。
2. TCC(Try-Confirm-Cancel)
TCC 是一种由业务开发者控制的分布式事务模式,强调“预处理 → 确认 → 回滚”的逻辑拆解。
每个参与系统都实现三套接口:
Try
: 预留资源(比如冻结库存)Confirm
: 真正提交(比如扣减库存)Cancel
: 失败回滚(比如解冻库存)
其工作流程不涉及到协调者,而是先让所有参与者进行预处理,执行资源预留,如果所有的参与者都 Try 成功,则进行 Confirm ,否则则进行回滚。
1️⃣ Try 阶段:
- 各参与者尝试执行资源预留,不真正变更资源;
- 保证幂等、挂起(冻结)状态。
2️⃣ Confirm 阶段:
- 所有参与者都 Try 成功后,执行 Confirm,正式提交业务操作;
- 幂等、不可逆。
3️⃣ Cancel 阶段:
- 如果任意参与者 Try 失败,执行 Cancel 补偿;
- 清理预留资源(如解冻、释放)。
3. 事务消息(如RocketMQ事务消息)
通过引入消息中间件的事务消息机制,将发送消息与本地事务进行绑定,来确保分布式事务的正确执行。
工作流程:
1️⃣ 发送“半消息”(Prepared Message):
- 不立刻投递,标记为暂挂状态。
2️⃣ 执行本地事务:
- 如扣款、更新数据库;
- 返回结果:成功 / 失败。
3️⃣ 通知 MQ 提交或回滚:
- 如果本地事务成功 → MQ 正式投递消息;
- 如果失败 → MQ 丢弃半消息。
4️⃣ 宕机或未知状态:
- MQ 会反查发送方:本地事务执行成功没?(checkTransaction)
值得注意的是,如果本地事务执行成功,消费者会消费到投递的消息,但是如果该消息执行失败,不会导致上游已提交的状态进行回滚,因为该操作的正确有效执行是下游业务需要保证的,不属于上游业务需要考虑的范畴,事务消息只是确保如果本地事务执行失败,则下游业务就不会获取到该条业务消息,只有执行成功的时候才能获取该条业务消息,只有投递的消息是否执行成功,是下游方需要考虑的。