分布式事务
为什么要使用分布式事务
解决分布式系统中的多系统数据一致性问题
分布式事务实现方案
XA方案
也叫做两阶段提交事务方案,其中有一个事务管理器的概念,负责协调多个数据库(资源管理器)的事务,事务管理器先询问各个数据库是否准备完成,如果每个数据库都回复ok,那么就正式提交事务,在各个数据库上执行操作,如果任何一个数据库回答false,那么就会滚事务
XA方案使用场景
这种分布式事务方案,比较适合单块应用里,跨多个库的分布式事务,而且因为严重依赖数据库层面搞定复杂的事务,效率很低,绝对不适合高并发的场景,可以基于Spring + JTA的方式实现一个简单的Demo
TCC方案
TCC的全过程是:Try, Confirm, Cancel
整个过程其实使用到了补偿的概念,分为三个阶段:
- try阶段:这个极端说的是各个服务的额自愿做检测以及对资源进行锁定或者预留
- Confirm阶段,这个阶段说的是在各个服务中执行实际的操作
- Cancel阶段:如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功的业务逻辑的回滚操作
这种方案其实用处也比较少,因为这个事务回滚实际上严重依赖于用代码实现,会造成补偿代码巨大,代码冗杂
比较适合的场景
这个就是除非真的一致性要求太高,是系统重核心之核心的场景,比如常见的就是资金类的场景,那就可以使用TCC方案,自己编写大量的业务逻辑,进行判断事务中的每个环节是否ok,不ok就执行补偿/回滚代码,而且最好是各个业务执行的时间都比较短
本地消息表
这种实现方案主要是通过在本地维护一张第三方消息表来进行事务的管理控制
大致流程为:
- A系统在自己本地一个事务里进行操作的同时,插入一条数据到消息表
- 接着A系统将这个消息发送到MQ中去
- B系统接收到消息之后,在一个事务里,往自己本地消息表里插入一条数据,同时执行其他的业务操作,如果这个消息已经被处理过了,那么此时这个事务会回滚,这样保证不会重复处理消息
- B系统执行成功之后,那么就会更新自己本地的消息表的状态以及A系统的消息标的状态
- 如果B系统处理失败了,那么就不会更新消息表中的状态,那么此时A系统会定时扫描自己的本地消息表,如果有没处理的消息,会再次发送到MQ中,让B再次处理
- 这个方案保证了最终一致性,哪怕B事务失败了,但是A会不断重发消息,直到B那边成功为止
使用场景
这个方案最大的问题就是严重依赖于数据库的消息表来管理事务,这个再高并发场景会比较差,所以一般也很少用
可靠消息最终一致性方案
这个的意思就是干脆不用本地的消息表了,直接基于MQ来实现事务,比如阿里的RocketMq就支持消息事务
大致的流程为:
- A系统先发送一个prepared消息到MQ,如果这个prepared消息发送失败,那么直接就取消操作别执行了
- 如果这个消息发送成功过了,那么接着执行本地事务,如果成功就告诉MQ发送确认消息,如果失败就告诉MQ回滚消息
- 如果发送了确认消息,那么此时B系统会接受到确认消息,然后执行本地的事务
- MQ会自动定时轮询所有的prepared消息回调接口,获取本地事务的处理结果,如果消息处理成功,则提交该事务,结束事务流程,如果回滚,那么本地服务也进行回滚
- 这个处理方案里,如果B系统的事务失败了,那么A系统监听不到本地事事务库中的事务成功状态,就会持续轮询,向B系统发送消息,如果是涉及到重要的资金类业务操作,那么就由B系统发送回滚通知,所有消息回滚
使用场景
这个来讲再以前还是比较合适的,很多大公司都在使用这种方式,RocketMQ支持,或者基于ActiveMQ,RabbitMQ自己封装一套逻辑,思路大体是这样
最大努力通知方案
这个方案的大致意思是
- 系统A本地事务执行完之后,发送个消息到MQ
- 这里会有一个专门消费MQ的最大努力通知服务,这个服务会消费MQ,然后写入数据库中记录下来,或者是放入个内存队列也可以,接着调用系统B的接口
- 要是系统B执行成功就ok了,要是系统B执行失败了,那么最大努力通知服务就定时尝试重新调用系统B,反复N次,最后还是不行就放弃
使用场景
这种方案在一定程度上允许少数分布式事务失败,一般用在对于分布式事务要求不严格的情况下,比如记录日志以及状态等
总结
其实在一个项目中,需要用到分布式事务相关的地方还是比较少,因为通过上面的几个方案来看,任何一个地方使用分布式事务,都会导致相关位置的代码复杂度提升十倍,同时会极大的降低系统的吞吐量以及性能
所以在线上其实99%的分布式事务场景,尽量不要做分布式事务,一般来说会使用监控(当问题产生时,发送邮件,短信等),记录好日志(一旦出错,记录好完整的日志),事后快速的定位,排查和出解决方案,修复数据等
其实在线上系统中,一般每几个月或者某个周期,都会安排人工筛查修复数据,一般都是临时写个程序或者根据数据问题情况进行删除或者修改,所以在使用分布式事务时一定要着重考虑