分布式 分布式事务
本文将介绍分布式事务及其解决方案。
一、什么是分布式事务?
在分布式系统中,一次操作需要由多个服务协同完成,这种由多个服务通过网络协同完成的事务便称为分布式事务。
二、分布式事务解决方案
1. 2PC
2PC,即两阶段提交,将事务的提交过程分为准备、提交两个阶段。
具体来说:
- 准备(Prepare):
- 协调者向所有参与者发送 Prepare 请求
- 各个参与者执行本地事务,记录日志
- 各个参与者反馈执行结果
- 提交(Commit ):
- 如果协调者接收到所有参与者的 Prepare 请求的成功反馈,
- 给所有参与者发送 Commit 请求
- 各个参与者执行事务提交
- 各个参与者反馈执行结果
- 协调者等待直至所有参与者已反馈,完成事务
- 否则,
- 给所有参与者发送 Roolback 请求
- 各个参与者执行事务回滚
- 各个参与者反馈执行结果
- 协调者等待直至所有参与者已反馈,取消事务
- 如果协调者接收到所有参与者的 Prepare 请求的成功反馈,
2PC 存在的缺点:
- 单点故障:需要一个中心化的协调者来管理事务,存在单点问题
- 同步阻塞:进入第二个阶段需要等待所有参与者的结果,如果某一个参与者执行缓慢,将导致所有的参与者都被阻塞
2. 3PC
3PC,即三阶段提交,它是 2PC 的改进版本,增加了额外的预备阶段,引入了超时机制。
具体来说:
- 预备(CanCommit ):
- 协调者向各个参与者发送 CanCommit 请求
- 各个参与者预估是否可以执行,反馈
- 准备(PreCommit ):
- 如果协调者接收到所有参与者的 CanCommit 请求的 Yes 反馈,
- 向所有参与者发送 PreCommit 请求
- 各个参与者执行本地事务,记录日志
- 各个参与者反馈执行结果
- 否则,
- 向所有参与者发送 Abort 请求,中断事务
- 如果协调者接收到所有参与者的 CanCommit 请求的 Yes 反馈,
- 提交(DoCommit ):
- 如果协调者接收到所有参与者的 PreCommit 请求的成功反馈,
- 向所有参与者发送 DoCommit 请求
- 各个参与者执行事务提交
- 各个参与者反馈执行结果
- 协调者等待直至所有参与者已反馈,完成事务
- 否则,
- 向所有参与者发送 Abort 请求
- 各个参与者执行事务回滚
- 各个参与者反馈执行结果
- 协调者等待直至所有参与者已反馈,中断事务
- 如果参与者等待一定时间后仍未收到 DoCommit 请求,执行事务提交
- 如果协调者接收到所有参与者的 PreCommit 请求的成功反馈,
与 2PC 相比的优点:
- 避免阻塞:通过预备阶段来确保事务的所有参与者对于事务的完成持正面态度,从而可以在迟迟未收到 DoCommit 请求时自行执行事务提交,避免了某个参与者执行缓慢导致整体阻塞
3. TCC
TCC,即 Try、Confirm、Cancel,它要求每个操作都应该实现三个子操作,具体来说:
- Try 预处理:做检查和资源预留
- Confirm 确认:执行业务操作;实现该子操作时应该考虑幂等性
- Cancel 取消:执行回滚操作;实现该子操作时应该考虑幂等性、空回滚
TCC 的缺点是:
- 要求实现三个子操作,对代码侵入性强,提高了开发成本
4. 可靠消息最终一致性
在实际开发中,服务与服务之间的可以通过 “直接调用” 的形式进行同步协作,也可以通过 “基于消息队列通信” 的形式进行异步协作。
可靠消息一致性针对的是第二个场景,其目的是确保 本地任务的执行结果 和 消息的投递结果 保持一致性。也就是说,一旦业务动作成功,则消息一定要成功投递。
具体来说:
- 生产者会预先将消息发送到消息队列
- 消息队列接受到消息后,持久化存储,并将存储结果反馈给生产者
- 生产者接收到存储成功的反馈后,执行事务
- 若事务执行成功,则消息队列将消息状态修改为待发送;否则,删除消息
- 消费者消费消息,并进行 ACK 确认
一般有两种实现方案:
- 本地消息表 + 消息队列:生产者在本地维护一个消息表,并保证其中消息与事务的一致性;通过定时任务等方式,将 “可发送” 状态的消息发送至消息队列
- 支持分布式事务的消息队列:直接使用提供原生支持的消息队列
5. 最大努力通知
简而言之,生产者尽最大努力 确保消息发送到消费者 ,具体做法有:
- 消息重复:当消费者无应答时,通过重试机制重复通知,直至一定阈值
- 消息校对:提供一个能力,供消费者主动查询消息