刚性事务属于 CAP 理论中的 CP 组合,会有性能上限,无法满足高并发场景的需求。基于 BASE 理论,柔性事务方案被提出用于保证事务数据的最终一致性。柔性事务本质是对 XA 协议的妥协,它通过降低强一致性要求,从而降低数据库资源锁定时间,提升可用性,允许有中间状态,要求最终一致性,也就是 AP 组合。柔性事务分为通知型和补偿型:
- 通知型事务都是异步的,包含有:可靠消息、最大努力通知两种
- 补偿型事务都是同步的,包含有:AT、TCC、Saga
可靠消息
可靠消息方案是指当事务发起方(消息发送者)执行完成本地事务后并发出一条消息,事务参与方(消息接收者)一定能够接收消息并处理事务成功
此方案强调的是一旦消息发给事务参与方,则最终事务要达到一致。这其中有两个关键问题:
- 本地事务与消息发送的原子性问题:若事务发起方在本地事务执行成功,则消息必须发出(可以是立即发送,也可以是异步发送),否则就无消息
- 事务参与方接收消息的可靠性问题
- 参与方必须能够从 MQ 接收到消息,如果接收消息失败可以重复接收消息
- 参与方要解决消息重复消费的问题(消费处理的幂等性)
可靠消息方案适合执行周期长且实时性要求不高的场景。引入消息机制后,原先同步的事务操作变为基于消息写/读的异步操作,避免了同步阻塞,也实现了服务间的解耦。一般有基于本地消息表和基于消息中间件这两种实现式
本地消息表
本地消息表核心思路是:将分布式事务拆分成本地事务进行处理

优点: 从应用设计开发的角度实现了消息数据的可靠性,消息数据的可靠性不依赖于消息中间件,弱化了对 MQ 中间件特性的依赖,方案轻量,容易实现。
缺点: 需设计 DB 消息表,同时还需要一个后台任务,不断扫描本地消息。导致消息的处理和业务逻辑耦合额外增加业务方的负担
事务消息
为了解决本地消息表与业务耦合的问题,利用消息队列的事务消息的能力,可理解为将本地消息表移动到了 MQ 内部

事务消息发送步骤如下:
- 发送方将半事务消息发送至 RocketMQ
- MQ 将消息持久化成功之后,向发送方返回 ACK 确认消息已经发送成功,此时消息为半事务消息
- 发送方开始执行本地事务逻辑
- 发送方根据本地事务执行结果向服务端提交二次确认(Commit 或是 Rollback),服务端收到 Commit 状态则将半事务消息标记为可投递,订阅方最终将收到该消息;服务端收到 Rollback 状态则删除半事务消息,订阅方将不会接受该消息
事务消息回查步骤如下:
- 在断网或者是应用重启的特殊情况下,上述发送步骤的步骤 4 提交的二次确认最终未到达服务端,经过固定时间后服务端将对该消息发起消息回查
- 发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果
- 发送方根据检查得到的本地事务的最终状态再次提交二次确认,服务端仍按照发送步骤的步骤 4 对半事务消息进行操作
最大努力通知
最大努力通知型的目标是 事务发起方尽量将业务处理结果通知到参与方。适用于一些最终一致性时间敏感度低,且参与方的处理结果不影响发起方的处理结果的这类通知类的业务场景。如短信供应商的回执通知:

最大努力通知型的实现方案,一般符合以下两个特点:
- 消息重复通知,在业务活动发起方完成业务处理之后,向参与方发送消息,参与方可能没有接收到通知,此时要发起方有一定的机制对消息重复通知(通常是发起方调用参与方的 http 接口,而且会协商一个 N 次 通知的上限)
- 定期校对,事务发起方提供消息校对的接口,如果事务参与方没有接收到发起方发送的消息,可以调用事务发起方提供的接口主动获取消息
总结
-
可靠性的保障方不同
- 可靠消息方案中,发起方需要保证将消息发出去,并且将消息发到参与方,消息的可靠性关键由发起方来保证
- 最大努力通知方案中,发起方尽最大努力将业务处理结果通知给参与方,但参与者是可能接收不到消息,此时需要接收通知方主动调用发起通知方的接口查询业务处理结果,消息通知的可靠性关键在参与方
-
两者的业务应用场景不同
- 可靠消息方案 关注的是整体业务处理的事务一致,以异步的方式完成整个业务处理,通常是内部系统之间的调用
- 最大努力通知 关注的是业务处理后的通知事务,即将业务处理结果可靠的通知出去
-
技术解决方向不同
- 可靠消息方案 要解决消息从发出到接收的一致性,即消息发出并且被接收到
- 最大努力通知方案 无法保证消息从发出到接收的一致性,只提供消息接收的可靠性机制。可靠机制是,尽最大努力将消息通知给参与方,当消息无法被参与方接收时,由参与方主动查询消息(业务处理结果)