分布式事务方案
本地消息表
流程
- A 系统在自己本地一个事务里操作同时,插入一条数据到消息表;
- A 系统将这个消息发送到 MQ 中去;
- B 系统接收到消息之后,在一个事务里,往自己本地消息表里插入一条数据,同时执行其他的业务操作,如果这个消息已经被处理过了,那么此时这个事务会回滚,这样保证不会重复处理消息;
- B 系统执行成功之后,就会更新自己本地消息表的状态以及 A 系统消息表的状态;
- 如果 B 系统处理失败了,那么就不会更新消息表状态,那么此时 A 系统会定时扫描自己的消息表,如果有未处理的消息,会再次发送到 MQ 中去,让 B 再次处理;
- 这个方案保证了最终一致性,哪怕 B 事务失败了,但是 A 会不断重发消息,直到 B 那边成功为止。
缺点
- 严重依赖于数据库的消息表来管理事务,这样在高并发的情况下难以扩展,
- 同时要在数据库中额外添加一个与实际业务无关的消息表来实现分布式事务,繁琐。
TCC
TCC是Try-Confirm-Cancel
的简称:
Try阶段:
完成所有业务检查(一致性),预留业务资源(准隔离性);
回顾上面航班预定案例的阶段1,机票就是业务资源,所有的资源提供者(航空公司)预留都成功,try阶段才算成功;
Confirm阶段:
确认执行业务操作,不做任何业务检查, 只使用Try阶段预留的业务资源。回顾上面航班预定案例的阶段2,美团APP确认两个航空公司机票都预留成功,因此向两个航空公司分别发送确认购买的请求。
Cancel阶段:
取消Try阶段预留的业务资源。回顾上面航班预定案例的阶段2,如果某个业务方的业务资源没有预留成功,则取消所有业务资源预留请求。
TCC与XA对比
相似
1) 在阶段1:
- 在XA中,各个RM准备提交各自的事务分支,事实上就是准备提交资源的更新操作(insert、delete、update等);
- 而在TCC中,是主业务活动请求(try)各个从业务服务预留资源。
2) 在阶段2:
-
XA根据第一阶段每个RM是否都prepare成功,判断是要提交还是回滚。如果都prepare成功,那么就commit每个事务分支,反之则rollback每个事务分支。
-
TCC中,如果在第一阶段所有业务资源都预留成功,那么confirm各个从业务服务,否则取消(cancel)所有从业务服务的资源预留请求。
区别
- XA是资源层面的分布式事务,强一致性,在两阶段提交的整个过程中,一直会持有资源的锁。
XA事务中的两阶段提交内部过程是对开发者屏蔽的,事务管理器在两阶段提交过程中,从prepare到commit/rollback过程中,资源实际上一直都是被加锁的。如果有其他人需要更新这两条记录,那么就必须等待锁释放。
-
TCC是业务层面的分布式事务,最终一致性,不会一直持有资源的锁。
TCC中的两阶段提交并没有对开发者完全屏蔽,也就是说从代码层面,开发者是可以感受到两阶段提交的存在。在第二阶段,需要提供confirm/cancel接口(确认购买机票/取消预留)。try、confirm/cancel在执行过程中,一般都会开启各自的本地事务,来保证方法内部业务逻辑的ACID特性。其中:
-
try过程的本地事务,是保证资源预留的业务逻辑的正确性。
-
confirm/cancel执行的本地事务逻辑确认/取消预留资源,以保证最终一致性,也就是所谓的
补偿型事务
(Compensation-Based Transactions)。
由于是多个独立的本地事务,因此不会对资源一直加锁。
可靠最终一致性
RocketMQ事务消息
-
发送事务消息,RocketMQ将消息状态标记为Prepared,此时这条消息消费者是无法消费到的;
-
执行业务代码逻辑,本地事务;
-
确认发送消息,RocketMQ将消息状态标记为可消费,这个时候消费者,才能真正的保证消费到这条数据;
-
如果确认消息发送失败了怎么办?RocketMQ会定期扫描消息集群中的Prepared消息,它会向生产者确认。
-
RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息,这样就保证了消息发送与本地事务同时成功或同时失败。
不支持事务的MQ
- 将消息先发送到一个一个”独立消息服务”应用中,刚开始处于prepare状态;
- 业务逻辑处理成功后,确认发送消息,这个时候”独立消息服务”才会真正的把消息发送给消息队列;
- 消费者消费成功后,ack时,除了对消息队列进行ack,对于独立消息服务也要进行ack,”独立消息服务”一般是把这条消息删除;
- 定时扫描prepare状态的消息,向消息发送端(生产者)确认的工作也由独立消息服务来完成;
最大努力通知型
最大努力通知型( Best-effort delivery)是最简单的一种柔性事务,适用于一些最终一致性时间敏感度低的业务,且被动方处理结果不影响主动方的处理结果。典型的使用场景:如银行通知、商户通知等。最大努力通知型的实现方案,一般符合以下特点:
-
不可靠消息:业务活动主动方,在完成业务处理之后,向业务活动的被动方发送消息,直到通知N次后不再通知,允许消息丢失(不可靠消息)。
-
定期校对:业务活动的被动方,根据定时策略,向业务活动主动方查询(主动方提供查询接口),恢复丢失的业务消息。