分布式事务

分布式事务相关知识整理。

处理模式

一般分布式事务处理模式包括:2阶段提交、3阶段提交、TCC(Try-Confirm-Cancel)、可靠消息(消息队列、数据库表)、SAGAS长事务、补偿性事务。具体采用哪一种分布式事务处理模式,需要根据自己业务场景来选择合适的机制。
duboo、spring cloud都可以算作是SOA框架,分布式事务控制支持依赖其他组件,例如通过JTA(2阶段、3阶段)、ActiveMQ消息中间件、ByteTCC(TCC)等。
zookeeper、redis可以支持分布式锁,分布式锁是分布式事务的核心,但分布式锁不等同于分布式事务。
redis对分布式锁的支持主要通过setnx,在使用redis分布式锁时候,一定要注意处理加锁客户端异常导致锁资源没有正常释放的情况(例如调用端down掉),在调用setnx时候需要加上对锁超时时间的判断
zookeeper对分布式锁的支持可以直接使用zookeeper curator-recipes客户端。

优缺点比对表

解决方案一致性协议优缺点优缺点应用场景
两阶段提交协议(2PC)强一致性XA协议,由Tuxedo提出1、 同步阻塞问题;2、 单点故障;1、极端情况下数据的不一致性;2、引入事务管理者(协调者),单点故障;3、系统可伸缩性存在问题;4、全局事务结束才能释放资源,性能问题;在高并发场景下很少使用。
三阶段提交协议(3PC)强一致性XA协议,由Tuxedo提出1、超时机制解决同步阻塞问题;2、预备阶段尽可能提早发现问题;1、极端情况下数据的不一致性;2、引入事务管理者(协调者),单点故障;3、系统可伸缩性存在问题;4、全局事务结束才能释放资源,性能问题;在高并发场景下很少使用。
TCC 模式(Try、Confirm、Cancel)可以理解为SQL事务中的 Lock、Commit、Rollback最终一致性阿里提出1、对于事务恢复,基于 Quartz调度,按照一定频率进行重试。2、要按具体业务来实现,业务耦合度较高,提高了开发成本。部分控制的好处是并发量和性能很好,缺点是数据一致性减弱了,完全控制则是牺牲了性能,保障了一致性,具体用哪种方式,最终还是取决于业务场景。作为技术人员,一定不能忘了技术是为业务服务的,不要为了技术而技术,针对不同业务进行技术选型也是一种很重要的能力!tcc-transaction 框架,@Compensable 注解
补偿模式最终一致性1、重试机制:同步通知、消息队列、定时任务。2、每次更新的时候进行自我恢复和修正。3、定时校对:未完成的定时重试、定时核对。世界上解决一个计算机问题最简单的方法:“恰好”不需要解决它!如果系统要实现回滚流程的话,有可能系统复杂度将大大提升,且很容易出现 Bug,估计出现 Bug 的概率会比需要事务回滚的概率大很多。可以考虑当出现这个概率很小的问题,能否采用人工解决的方式。
可靠事件模式最终一致性1、正向发送消息:消息持久化到本地数据库,标志状态:待发送、结束、已发送、完成等。2、正向接受消息:ACK机制。3、逆向消息:弥补消息主动丢弃。4、补偿机制:定时任务扫描“未完成”的消息并重新投递。缺点:1、一次消息发送需要两次网络请求(half 消息+ commit/ rollback消息) 。2、业务处理服务需要实现消息状态回查接口。

共性点:
1、 如果最终多次重试失败可以根据相关日志并主动通知开发人员进行手工介入。
2、 业务处理逻辑需要保证幂等。

两阶段提交(2PC)

第一阶段是表决阶段,所有参与者都将本事务能否成功的信息反馈发给协调者;
第二阶段是执行阶段,协调者根据所有参与者的反馈,通知所有参与者,步调一致地在所有分支上提交或者回滚;

缺点:
单点问题:事务管理器在整个流程中扮演的角色很关键,如果其宕机,比如在第一阶段已经完成,在第二阶段正准备提交的时候事务管理器宕机,资源管理器就会一直阻塞,导致数据库无法使用。
同步阻塞:在准备就绪之后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源。
数据不一致:两阶段提交协议虽然为分布式数据强一致性所设计,但仍然存在数据不一致性的可能。比如:在第二阶段中,假设协调者发出了事务 Commit 的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了 Commit 操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。
总的来说,XA 协议比较简单,成本较低,但是其单点问题,以及不能支持高并发(由于同步阻塞)依然是其最大的弱点。

补偿事务(TCC)

核心思想是针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作,它大致分为三个阶段:
(1)Try 阶段主要是对业务系统做检测及资源预留
(2)Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。
(3)Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。

优点: 跟 2PC 比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些
缺点: 缺点还是比较明显的,在2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理。

TCC 的实践经验
蚂蚁金服TCC实践,总结以下注意事项:

1
2
3
4
5
➢业务模型分2阶段设计
➢并发控制
➢允许空回滚
➢防悬挂控制
➢幂等控制

本地消息表(异步确保)

它的核心思想是将需要分布式处理的任务通过消息或者日志的方式来异步执行,消息或日志可以存到本地文件、数据库或消息队列,再通过业务规则进行失败重试,它要求各服务的接口是幂等的。
如果消息消费方执行失败则发送失败消息给生产方,进行相应的业务补偿,实现最终一致性。

优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。
缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会增加很多工作量。
这个方案的核心在于第二阶段的重试和幂等执行。失败后重试,这是一种补偿机制,它是能保证系统最终一致的关键流程。

MQ 事务消息

不要用本地的消息表了,直接基于MQ来实现事务。比如阿里的RocketMQ就支持消息事务。

大概流程:
A系统先发送一个prepared消息到mq,如果这个prepared消息发送失败那么就直接取消操作别执行了
如果这个消息发送成功过了,那么接着执行本地事务,如果成功就告诉mq发送确认消息,如果失败就告诉mq回滚消息
如果发送了确认消息,那么此时B系统会接收到确认消息,然后执行本地的事务
mq会自动定时轮询所有prepared消息回调你的接口,问你,这个消息是不是本地事务处理失败了,所有没发送确认消息?那是继续重试还是回滚?一般来说这里你就可以查下数据库看之前本地事务是否执行,如果回滚了,那么这里也回滚吧。这个就是避免可能本地事务执行成功了,别确认消息发送失败了。
这个方案里,要是系统B的事务失败了咋办?重试咯,自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如B系统本地回滚后,想办法通知系统A也回滚;或者是发送报警由人工来手工回滚和补偿

目前国内互联网公司大都是这么玩儿的,要不你使用RocketMQ支持的,要不你就基于其他MQ中间件自己封装一套类似的逻辑,总之思路就是这样的。

AT模式

AT 模式是一种无侵入的分布式事务解决方案。
阿里seata框架,实现了该模式。

在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。

参考

dubbo、zookeeper、spring cloud、redis都能否做分布式事务控制?:https://www.zhihu.com/question/56839867
分布式事务解决方案的对比:blog.sina.com.cn/s/blog_3fba24680102z0w4.html
分布式事务解决方案:https://www.jianshu.com/p/ee4071d0c951
分布式事务的4种模式:https://zhuanlan.zhihu.com/p/141645172

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×