1. 2PC(两阶段提交)
1.1 原理
- 两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段和提交阶段
- 准备阶段:事务管理器给每个参与者发送prepare消息,每个数据库参与者在本地执行事务,并在写本地的undo/redo日志,此时事务没有提交(但需要锁定资源)
- 提交阶段:如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚消息;否则发送提交消息。
1.2 缺点
- 同步阻塞问题:执行过程中,所有参与节点都是事务阻塞型的。数据库本地事务只有在第二阶段(提交阶段)才会进行提交或回滚。(性能差)
- 单点故障问题:协调者发生故障,则参与者会一直阻塞下去。或者提交阶段发生错误,则会导致有些参与方的回滚和提交无法完成,从而造成数据不一致性或者参与方一直锁定资源不释放。
- 适用于分布式数据库层面的事务,但是不适合业务层面的分布式事务。
1.3 Seata实现的2PC特点
- seata会在数据库中创建一个undo_log表
- 在准备阶段就会将本地事务给提交。在写数据的同时也会写undo_log表。因为是在本地事务中的,所以保证了写数据和写undo_log表是原子性操作。
- 在提交阶段,如果事务管理器收到了参与者执行失败或者超时消息时,会给每个参与者发送回滚消息,回滚是通过根据undo_log表中的记录反向更新数据; 否则发送提交消息,即删除undo_log表中的相关记录。
- 主要解决了传统2PC长期锁资源的问题。
2. TCC
2.1 概念
- TCC分为三个阶段
- Try阶段:业务检查一致性或者资源预留。
- Confirm阶段:Try阶段所有的分支事务都成功后会执行Confirm。通常情况下,采用TCC则任务Confirm阶段是不会出错的。所以只要Try成功,Confirm就一定要成功,如果Confrim不成功,需要进行重试,重试还不行就要进行人工处理。
- Cancel阶段:在业务执行错误需要回滚的状态下,执行分支事务的业务取消了,需要进行资源释放。通常采用TCC则认为,若Try失败后则Cancel阶段也是一定要成功的。如果Cancel不成功,需要进行重试,重试还不行就要进行人工处理。
2.2 优点
- TCC不存在资源阻塞问题,因为这三个方法都是直接进行本地事务的提交的。一旦出现异常则通过Cancel来进行回滚补偿。也就是常说的补偿性事务。
- 因为是在业务层面上实现的,所以TCC可以跨数据库,跨不同的业务系统来实现事务。
2.3 缺点
- TCC对业务的代码侵入较大和业务紧耦合。需要考虑空回滚,幂等,悬挂等问题。
- 空回滚:try没执行就调用cancel方法,常常因为分支事务所在服务宕机,解决方法时要识别出这个空回滚,如在try方法里插入一条特殊记录,表示try阶段执行了。然后在cancel阶段去读取该记录,若存在,则正常回滚,否则进行空回滚
- 悬挂:Cancel接口比Try接口先执行;解决方法:在try也要判断,是否Cancel已经执行了,如果执行了则这个阶段也啥也不做。
3. 可靠消息最终一致性
3.1 概念
- 当事务发起方执行完成本地事务后并发出一条消息,事务参与方(消息消费者)一定能够接收消息并处理事务成功,此方案强调的是只要消息发给事务参与方最终事务要达到一致。
3.2 需要解决的问题
- 本地事务与消息发送的原子性问题:解决办法是不直接发送给mq,而是打日志数据库。然后定时任务去扫描日志数据库去发送消息
- 事务参与方接收消息的可靠性:事务参与方必须一定能够收到消息,如果接收消息失败可以重复接收消息。解决办法通过消息队列的ack机制
- 消息重复消费的问题:事务参与方消费消息需要保证幂等性。
3.3 解决方案一:本地消息表方案
- 本地事务保证数据业务操作和日志消息操作的原子性,然后通过定时任务将消息发送至消息中间件,待确认消息发送给消费方成功再将日志消息删除
3.4 解决方案二:RocketMQ事务消息方案
- RocketMQ4.3后实现了完整的事务消息,实际上其实是对本地消息表的一个封装,将本地消息表移动到了MQ内部,解决 Producer 端的消息发送与本地事务执行的原子性问题
- 发送方在本地事务中,向mq中发送半消息(目前处于不可投递状态),收到mq的ack后,再执行并提交本地事务。
- 发送方提供事务回查接口,mq会定时调用该接口反查事务状态。mq根据该接口返回的结果来决定要否要投递消息。
4. 最大努力通知
4.1 概念
- 被调用方会经最大努力向调用方发送通知。(通过消息重试通知机制)
- 如果实在通知不到,被调用方还提供了查询接口,调用方可以主动调用查询接口来得到结果。
- 最大努力通知需要提供以下功能
- 消息重复通知机制
- 消息校对机制
4.1 最大努力通知与可靠消息最终一致性区别
- 解决方案思想不同
- 可靠消息一致性,是由发送方保证将消息发出去,并且将消费发送到接收方。消息的可靠性关键由发送方来保证
- 最大努力通知,发送方只是尽最大努力将业务处理结果通知给接收方,但是可能消息接收不到,此时需要接收通知方主动调用发起通知方的接口查询业务处理结果,通知的可靠性关键在接收通知方。
- 两种的业务场景不同
- 可靠消息一致性,一般用于交易过程,以异步的方式完成交易
- 最大努力通知,一般用于交易后的通知事务