分布式事务
# 分布式事务的实现方式
首先需要先指定CAP理论。
- C consistency 一致性: 所有节点同一时间具有相同的数据
- A availability 可用性: 节点可用,每个请求都能收到非错误性的响应
- P partition tolerance 分区容错性: 节点不能因网络分区而崩溃
在分布式系统中,CAP理论的一个限制是: CAP理论的实现中,只能同时满足两个条件。而 P 是必须的,我们通常会选择 AP,牺牲 C 强一致性。
通常放弃强一致性(如 2PC/XA),转而追求最终一致性(Eventual Consistency)
实现方案大致如下:
# 1、基于“本地消息表” (Local Message Table)
这是最经典、最不依赖特定中间件特性的方案,适用于对一致性要求极高的场景 。
具体流程如下:
- 利用数据库的分布式事务,保证原子性。(业务数据+消息记录 在同一个事务里面完成,成功提交,失败则回滚)
- 定时任务扫描,独立后台任务轮询扫描待处理的消息,最后更新。
使用定时任务扫描,可以提高对接口的容错性,从而提升系统的可用性。
优点:不依赖第三方中间件,可靠性极高。实现简单,容易排查回溯,适用于大多数场景。 缺点:实时性差,有延迟,定时任务对数据库压力颇大
# 2、MQ 消息中间件
基于“事务消息” (Transactional Messaging)
这是目前互联网大厂(特别是阿里系技术栈)非常主流的方案,典型代表是 RocketMQ。它其实是“本地消息表”的封装和优化。
核心在于它解决了本地事务与消息发送的原子性问题,即保证:
业务操作成功,消息一定能被消费
业务操作失败,消息一定不会被消费
核心流程(RocketMQ 为例):
- 发送半消息(Half Message):生产者向 MQ 发送一条“半消息”,MQ 收到后持久化但不投递(消费者看不见)。
- 执行本地事务:生产者执行本地业务逻辑。
- 提交/回滚:
- 如果本地事务成功,向 MQ 发送 Commit,MQ 将消息改为“可投递”,消费者收到消息。
- 如果本地事务失败,向 MQ 发送 Rollback,MQ 删除消息。
- 回查机制(关键点):如果 MQ 长时间没收到 Commit/Rollback(比如网络断了),MQ 会反向回调生产者的接口,检查本地事务的状态,从而决定是提交还是回滚。
┌─────────┐ 1.发送Half消息 ┌─────────┐
│ Producer│───────────────────────▶│ MQ Server │
└────┬────┘ └─────┬───┘
│ 2.返回发送结果 │
│◀─────────────────────────────────│
│ │
│ 3.执行本地事务 │
│ (数据库操作) │
│ │
│4.提交/回滚事务状态 │
│─────────────────────────────────▶│
│ │
│ │5.MQ定时检查
│ │ 事务状态
│ │ (回查机制)
│◀─────────────────────────────────│
│ │
│6.返回最终事务状态 │
│─────────────────────────────────▶│
│ │
│ │7.提交/回滚消息
│ │ (对消费者可见/删除)
│ │
│ ▼
┌────┴────┐ ┌─────────┐ 8.消费消息
│Producer │ │ MQ Server│─────────────────┐
│本地事务 │ │ │ │
└─────────┘ └─────────┘ │
▼
┌─────────┐
│Consumer │
└─────────┘
优点:业务解耦,不需要维护本地消息表,性能更好。
缺点:强依赖支持事务消息的 MQ 中间件(RabbitMQ、Kafka 原生不支持这种强一致性逻辑,需要魔改)。
# 方案三:Saga 模式 (长事务编排)
当你的业务本身就是由多个步骤组成的长时间过程时,Saga是比TCC更轻量、更适合最终一致性的模式。
通过将大事务分解为一系列有序的、可以独立执行的本地事务来管理一致性。这些本地事务通过协调器或者事件驱动的方式依次执行,如果其中一个事务失败,则使用相应的补偿事务来撤销之前已经完成的事务,以确保系统的一致性,属于补偿性事务。
上次更新: 2025-12-04 03:34:09