阿里分布式事务组件 fescar/seata 对 XA 2PC 的改进及其设计思想
Posted cafebean
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了阿里分布式事务组件 fescar/seata 对 XA 2PC 的改进及其设计思想相关的知识,希望对你有一定的参考价值。
1. 二阶段提交协议的由来
X/Open 组织提出了分布式事务处理的规范 DTP 模型(Distributed Transaction Processing),该模型中主要定义了三个基本组件,分别是
应用程序(Application Program ,简称AP):用于定义事务边界(即定义事务的开始和结束),并且在事务边界内对资源进行操作。
资源管理器(Resource Manager,简称RM):如数据库、文件系统等,并提供访问资源的方式。
事务管理器(Transaction Manager ,简称TM):负责分配事务唯一标识,监控事务的执行进度,并负责事务的提交、回滚等。
一般,我们称TM为事务的协调者,而称RM为事务的参与者。TM 与 RM 之间的通信接口,则由 XA 规范来约定。
在 DTP 模型的基础上,才引出了二阶段提交协议来处理分布式事务。
2. 二阶段提交基本算法
2.1 前提
二阶段提交协议能够正确运转,需要具备以下前提条件:
存在一个协调者,与多个参与者,且协调者与参与者之间可以进行网络通信
参与者节点采用预写式日志,日志保存在可靠的存储设备上,即使参与者损坏,不会导致日志数据的消失
参与者节点不会永久性损坏,即使后仍然可以恢复
实际上,条件2和3所要求的,现今绝大多数关系型数据库都能满足。
2.2 基本算法
2.2.1 第一阶段
协调者节点向所有参与者节点询问是否可以执行提交操作,并开始等待各参与者节点的响应。
参与者节点执行询问发起为止的所有事务操作,并将Undo信息和Redo信息写入日志。
各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个"同意"消息;如果参与者节点的事务操作实际执行失败,则它返回一个"中止"消息。
2.2.2 第二阶段
当协调者节点从所有参与者节点获得的相应消息都为"同意"时:
协调者节点向所有参与者节点发出"正式提交"的请求。
参与者节点正式完成操作,并释放在整个事务期间内占用的资源。
参与者节点向协调者节点发送"完成"消息。
协调者节点收到所有参与者节点反馈的"完成"消息后,完成事务。
如下图所示:
如果任一参与者节点在第一阶段返回的响应消息为"终止",或者 协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时:
协调者节点向所有参与者节点发出"回滚操作"的请求。
参与者节点利用之前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。
参与者节点向协调者节点发送"回滚完成"消息。
协调者节点收到所有参与者节点反馈的"回滚完成"消息后,取消事务。
如下图所示:
3. 二阶段提交缺陷分析
二阶段提交协议除了协议本身具有的局限性之外,如果我们把以下情况也考虑在内:
协调者宕机
参与者宕机
网络闪断(脑裂)
那么二阶段提交协议实际上是存在很多问题的
3.1协议本身的缺陷
协议本身的缺陷是指,在协议正常运行的情况下,无论全局事务最终是被提交还是被回滚,依然存在的问题,而暂不考虑参与者或者协调者宕机,或者脑裂的情况。
3.1.1 性能问题
当网络闪断发生在第一阶段时,可能会有部分参与者进入阻塞状态,全局事务无法结束。
当发生在第二阶段时,可能发生部分参与者执行了 commit 而部分参与者未执行 commit,从而导致全局数据不一致的问题。
参与者的本地事务开启后,直到它接收到协调者的 commit 或 rollback 命令后,它才会提交或回滚本地事务,并且释放由于事务的存在而锁定的资源。不幸的是,一个参与者收到协调者的 commit 或者 rollback 的前提是:协调者收到了所有参与者在一阶段的回复。
如果说,协调者一阶段询问多个参与者采用的是顺序询问的方式,那么一个参与者最快也要等到协调者询问完所有其它的参与者后才会被通知提交或回滚,在协调者未询问完成之前,这个参与者将保持占用相关的事务资源。
即使,协调者一阶段询问多个参与者采用的是并发询问的方式,那么一个参与者等待收到协调者的提交或者回滚通知的时间,将取决于在一阶段过程中,响应协调者最慢的那个参与者的响应时间。
无论是哪一种情况,参与者都将在整个一阶段持续的时间里,占用住相关的资源,参与者事务的处理时间增加。若此时在参与者身上有其它事务正在进行,那么其它事务有可能因为与这个延迟的事务有冲突,而被阻塞,这些被阻塞的事务,进而会引起其它事务的阻塞。
总而言之,整体事务的平均耗时增加了,整体事务的吞吐量也降低了。这会使得整个应用系统的延迟变高,吞吐量降低,可扩展性降低(当参与者变多的时候,延迟可能更严重)。
总的来说,二阶段提交协议,不是一个高效的协议,会带来性能上的损失。
3.1.2 全局事务隔离性的问题
全局事务的隔离性与单机事务的隔离性是不同的。
当我们在单机事务中提到不允许脏读时,那么意味着在事务未提交之前,它对数据造成的影响不应该对其它事务可见。
当我们在全局事务中提到不允许脏读时,意味着,在全局事务未提交之前,它对数据造成的影响不应该对其它事务可见。
在二阶段提交协议中,当在第二阶段所有的参与者都成功执行 commit 或者 rollback 之后,全局事务才算结束。但第二阶段存在这样的中间状态:即部分参与者已执行 commit 或者 rollback,而其它参与者还未执行 commit 或者 rollback。此刻,已经执行 commit 或者 rollback 的参与者,它对它本地数据的影响,对其它全局事务是可见的,即存在脏读的风险。对于这种情况,二阶段协议并没有任何机制来保证全局事务的隔离性,无法做到“读已提交”这样的隔离级别。
3.2 协调者宕机
如果在第一阶段,协调者发生了宕机,那么因为所有参与者无法再接收到协调者第二阶段的 commit 或者 rollback 命令,所以他们会阻塞下去,本地事务无法结束,
如果协调者在第二阶段发生了宕机,那么可能存在部分参与者接收到了 commit/rollback 命令,而部分没有,因此这部分没有接收到命令的参与者也会一直阻塞下去。
协调者宕机属于单点问题,可以通过另选一个协调者的方式来解决,但这只能保证后续的全局事务正常运行。而因为之前协调者宕机而造成的参与者阻塞则无法避免。如果这个新选择的协调者也宕机了,那么一样会带来阻塞的问题。
3.3 参与者宕机
如果在第一阶段,某个参与者发生了宕机,那么会导致协调者一直等待这个参与者的响应,进而导致其它参与者也进入阻塞状态,全局事务无法结束。
如果在第二阶段,协调者发起 commit 操作时,某个参与者发生了宕机,那么全局事务已经执行了 commit 的参与者的数据已经落盘,而宕机的参与者可能还没落盘,当参与者恢复过来的时候,就会产生全局数据不一致的问题。
3.4 网络问题-脑裂
当网络闪断发生在第一阶段时,可能会有部分参与者进入阻塞状态,全局事务无法结束。
当发生在第二阶段时,可能发生部分参与者执行了 commit 而部分参与者未执行 commit,从而导致全局数据不一致的问题。
4. 三阶段提交
在二阶段提交中,当协调者宕机的时候,无论是在第一阶段还是在第二阶段发生宕机,参与者都会因为等待协调者的命令而进入阻塞状态,从而导致全局事务无法继续进行。因此,如果在参与者中引入超时机制,即,当指定时间过去之后,参与者自行提交或者回滚。但是,参与者应该进行提交还是回滚呢?悲观的做法是,统一都回滚。但事情往往没那么简单。
当第一阶段,协调者宕机时,那么所有被阻塞的参与者选择超时后回滚事务是最明智的做法,因为还未进入第二阶段,所以参与者都不会接收到提交或者回滚的请求,当前这个事务是无法继续进行提交的,因为参与者不知道其它参与者的执行情况,所以统一回滚,结束分布式事务。
在二阶段提交协议中的第二阶段,当协调者宕机后,由于参与者无法知道协调者在宕机前给其他参与者发了什么命令,进入了第二阶段,全局事务要么提交要么回滚,参与者如果引入超时机制,那么它应该在超时之后提交还是回滚呢,似乎怎么样都不是正确的做法。执行回滚,太保守,执行提交,太激进。
如果在二阶段提交协议中,在第一阶段和第二阶段中间再引入一个阶段,如果全局事务度过了中间这个阶段,那么在第三阶段,参与者就可以认为此刻进行提交的成功率会更大。但这难道不是治标不治本吗,当进入第三阶段,全局事务需要进行回滚时候,如果协调者宕机,那么参与者超时之后自行进行提交事务,就会造成全局事务的数据不一致。
再考虑参与者宕机的情况下,协调者应该在超时之后,对全局事务进行回滚。
总结起来,三阶段提交主要在二阶段提交的基础上,为了解决参与者和协调者宕机的问题,而引入了超时机制,并因为超时机制,附带引入中间这一层。
并且,三阶段提交并没有解决二阶段提交的存在的脑裂的问题。
总而言之,二阶段和三阶段提交都无法完美地解决分布式事务的问题。关于三阶段提交更详细的算法和步骤,可以参考我的另外一篇文章《分布式事务概览》
5. fescar/seata 二阶段提交
以上是关于阿里分布式事务组件 fescar/seata 对 XA 2PC 的改进及其设计思想的主要内容,如果未能解决你的问题,请参考以下文章
alibaba/fescar 阿里巴巴 开源 分布式事务中间件
ODF大会议题早知道:2018开源数据库论坛(ODF)演讲主题:阿里云数据库的分布式事务实践