• 即时消息服务框架(iMSF)应用实例之分布式事务三阶段提交协议的实现


    一,分布式事务简介

    在当前互联网,大数据和人工智能的热潮中,传统企业也受到这一潮流的冲击,纷纷响应国家“互联网+”的战略号召,企业开始将越来越多的应用从公司内网迁移到云端和移动端,或者将之前孤立的IT系统联网整合,或者将原来厚重的企业应用拆分重组,独立成一个个轻量级的应用对外提供服务,这对传统的业务处理的数据一致性,带来了严重的挑战,我们已经身处一个分布式的计算环境,分布式事务的需求越来越普遍。

    举一个例子,某行业电商网站经过几年的发展,业务数据累积越来越多,查询越来越慢。经过内部评审分析,认为系统的瓶颈就是数据库压力过大,如果要解决这问题,必须分表分库,比如将订单,商品,用户分布到不同的数据库去,但这样随之带来一个问题,原来处理业务的时候使用的是本地事务,分库后就需要使用分布式事务了。

    那么应该如何实现分布式事务呢?

    这里我们需要明确一点,并非数据库天然就是分布式的在执行操作的,事务都是在一个数据库实例上进行的,如果要执行一个分布式事务的操作,那么就需要协调多个分散的数据库上执行的事务操作。所以在分布式事务中,有2个概念:

    l  Distributed Transaction Resource Owner (简称DTR):

    n  --分布式事务资源服务器,拥有事务资源的服务器,如绝大部分关系数据库,一些消息队列,或者一些能够执行类似事务操作的应用。

    l  Distributed Transaction Coordinate Controller (简称DTC):

    n  --分布式事务协调控制器,它协调控制分布式事务环境中的事务资源服务器,发送指令给它们并且处理事务资源服务器返回的结果。

    二,分布式事务的实现层面

    在分布式事务的具体实现层面,可以在数据库层直接实现,也可以在应用服务层面实现。如果是在应用服务层面实现,本质上它也可能是调用本地的数据库事务。

    下面是DTR与DTC的拓扑关系图:

    基于关系数据库层面接口实现的分布式事务

    基于数据库的分布式事务,一般在应用程序的数据层调用系统的分布式事务组件,应用程序执行本地事务的时候,先选举出一个分布式事务协调器,然后协调器来协调各个本地事务的执行。由于是在应用程序的数据层进行的调用,所以它对远程数据库的操作是在本地进程内的。如果你的应用部署在多台服务器上,那么在每一台操作数据库的应用服务器上都要安装运行分布式事务协调器服务。

    基于应用服务层面实现的分布式事务

    基于应用服务层面的分布式事务,是在应用服务层面进行的事务控制,它同样会有分布式事务协调控制器,和事务资源服务器。与基于数据库层面的分布式事务不同,事务的类型支持更广泛,比如消息队列访问,文件写入或者具有补偿操作的业务应用程序,都可以成为事务资源,并且不要求这些事务资源本身支持分布式事务。举个例子,事务资源A是Windows上的SQLSERVER数据库,事务B是Linux上的MySQL数据库,这时候事务B就没法使用Windows上的事务协调控制器MSDTC了。而基于服务层面的分布式事务,可以解决这个问题。

    三,分布式事务的2阶段提交协议(2PC)

    第一阶段(1PC):提交投票阶段

    协调器向事务资源服务器发出 CanCommit 的是否可以提交事务的询问指令,事务资源服务器收到此指令后,准备好要提交的事务资源,再向协调器回复 YES;如果没有准备好,比如执行事务中的操作出现了错误,应该回复 NO.如果某DTR无法回复,DTC也认为该DTR的结果是NO.

    第一阶段,全部回复为YES,代表各个事务资源服务器均已经准备好了提交。

    第一阶段,事务资源服务器DTR-2回复为NO,如果DTC等待DTR-2超过设定时间都没有得到回复,或者DTR-2与DTC断开了连接,也认为DTR-2的结果是NO

    第二阶段(2 PC):提交或终止阶段

    协调器统计所有事物资源服务器的回复数量,如果全部回复为YES,则向所有事物资源服务器发出Commit指令,否则,发出Abort指令。资源服务器收到指令后,执行相应的操作。

    二阶段提交事务的数据不一致问题

    在第二阶段(2PC),如果DTR没有收到DTC的指令改怎么办呢?

    如果等到超时都没有收到DTC的指令,DTR处于“可以提交”或者“不可以提交”的双重状态,也就是提交状态不可知。假设DTR1没有收到DTC的提交指令或者撤销指令,DTR1可以假设DTR2也不会收到指令,因为此时大概率是DTC宕机或者网络整体不良,那么DTR1最佳的做法是回滚事务。

    但是,如果仅仅是DTR1受网络影响没有收到提交指令,而DTR2收到了提交指令,那么DTR1回滚事务,DTR2提交了事务,整个分布式事务就是失败的,数据发生了不一致。

    因此,2阶段提交的分布式事务不是高可靠的分布式事务控制模型,需要在事务资源的提交环节做更多的验证,这便是3阶段提交的分布式事务。

    不过,对于大部分系统,2阶段提交的分布式事务已经能够满足应用了,因为通常情况下,都是基于数据库应用层实现的分布式事务,并且各个事务资源节点都在同一个局域网内,发生网络不稳定的概率非常小,并且现在不少数据库都会做高可靠性的数据库集群,发生宕机的可能性也非常小,最终出现数据不一致的概率也就非常小了。

    如果系统的应用环境不能满足上面说的任何一个条件,即分布式事务的控制不是在数据库应用层,子系统不在一个局域网,或者数据库没有做高可靠的集群,并且对于系统的事务一致性要求非常高,那么应该使用3阶段提交协议来实现分布式事务。

    四,分布式事务的3阶段提交协议(3PC)

    对2阶段提交协议的分析我们发现,2PC的事务提交阶段状态是不确定的,整个事务容易出现不一致的情况。所以,我们队2PC的提交阶段,进一步拆分成“预提交”阶段和提交阶段,增加事务提交状态的确认过程。

    第一阶段(1PC):提交投票阶段

    协调器向事务资源服务器发出 CanCommit 的是否可以提交事务的询问指令,事务资源服务器收到此指令后,准备好要提交的事务资源,再向协调器回复 YES;如果没有准备好,比如执行事务中的操作出现了错误,应该回复 NO.如果某DTR无法回复,DTC也认为该DTR的结果是NO.

    该阶段的处理过程跟2阶段提交协议的第一阶段是一样的,处理流程图参考前面,此略。

    第二阶段(2 PC):预提交或终止阶段

    预提交事务

    协调器(DTC)统计所有事务资源服务器(DTR)的回复数量,如果全部回复为YES,则向所有事物资源服务器发出PreCommit指令,否则,发出Abort指令。资源服务器收到指令后,执行相应的操作。

    在第二阶段,如果DTR收到PreCommit指令,则向DTC回复ACK消息,表示收到了指令,准备提交,接着,进入第三阶段,等待最终的提交指令。

    终止事务

    在第二阶段,如果在第一阶段有节点异常,DTC发出撤销指令,DTR收到了撤销指令,那么它执行回滚本地事务的操作。如果由于网络原因,某个DTR一直等到超时都没有收到PreCommit指令,那么它执行Abort撤销指令,回滚本地事务。

    第三阶段(3 PC):提交或终止阶段

    提交分布式事务

    协调器(DTC)统计所有事务资源服务器(DTR)在第二阶段的回复数量,如果全部回复为ACK,则向所有DTR发出Commit指令。DTR收到指令后,执行事务提交操作,并返回Commit Done消息,DTC收到此消息,结束整个分布式事务过程。

     

    回滚分布式事务

    协调器(DTC)统计所有事务资源服务器(DTR)在第二阶段的回复数量,如果未收到全部回复为ACK,则它认为有节点可能出现了网络故障,此节点没有收到PreCommit指令或者虽然收到了却没有回复ACK,测试DTC应该向所有DTR节点发出撤销指令。各DTR收到撤销指令后,回滚本地事务,然后回复消息,DTC完成本次事务过程。

     

    三阶段提交事务也并不完美

    考察第3阶段的提交分布式事务的情况,DTR1收到了Commit指令,但是由于网络原因,DTR2没有收到此指令,那么DTR2是提交本地事务还是回滚本地事务?

    站在DTR2的角度,它在本阶段可能收到Commit指令,也可能收到Abort指令,那么它既可以提交本地事务也可以回滚本地事务,两种操作是不确定的,所以,3阶段提交协议,仍然不是完美的,不能百分之百保证数据的最终一致性。

    既然3阶段提交协议仍然有不确定性,那么相比2阶段提交协议有什么意义呢?

    仔细想下,DTR2已经进入第3阶段了,那么肯定其它DTR都进入了第3阶段,而进入第3阶段的前提是各DTR节点都收到过PreCommit指令,都是已经准备好提交只等最后的提交指令了,否则各节点在第二阶段应该收到撤销指令,不会再进入第三阶段。既然各DTR节点都进入了第三阶段,它们都准备好提交事务了,那么即使没有收到最终的Commit指令,DTC发出Commit指令也是大概率的。所以,从概率上讲,如果在第三阶段,DTR没有收到Abort撤销指令,也没收到Commit提交指令,那么它默认应该指向Commit指令,提交本地事务。相比第二阶段某DTR节点没有收到指令而认为应该收到PreCommit指令的概率,要大得多。

    关于第三阶段没有收到指令而应该大概率执行Commit指令的问题,理解起来可能有点困难,我给同事讲的时候大部分同事也难以理解,可能是我表述的问题,大家有更好的解释方式,欢迎交流,不胜感激!

    5,实现 3阶段提交的分布式事务

    本文将介绍一个基于服务层面而不是数据库层面的,3阶段提交的分布式事务中间件的设计开发过程。这个中间件必须解决下面几个问题:

    l  通信组件—分布式事务控制器(DTC),分布式事务资源服务器(DTR)都是独立的服务,这些服务部署在不同的通信节点,它们之间需要进行可靠的网络通信,因此通信组件是基础;

    l  数据访问组件—提供基础的数据读写操作,并且能够操作本地事务。

    l  服务组件—将DTC,DTR的功能代码编写为相应的SOA服务组件

    l  关系数据库—具有事务功能的关系数据库,可以是嵌入式的本地数据库,比如SQLite,也可以是服务器客户机模式的网络数据库,比如SQLSERVER。

    各组件的关系图如下:

    PDF.NET的消息服务框架(MSF)具有开发服务组件基础的接口和一套消息通信组件,同时还有一个服务容器,可以承载本篇文章说的分布式事务控制器DTC,分布式事务资源服务DTR这些服务应用,同时PDF.NET还有一个强大的数据访问组件 PDF.NET SOD,下一篇文章,我们将来具体讨论基于MSF和SOD的3阶段分布式事务应用的实现过程,它的源码已经发布在 https://github.com/bluedoctor/MSF-DistTransExample ,大家可以先睹为快。

  • 相关阅读:
    [leetCode]945.使数组唯一的最小增量
    [leetCode]974. 和可被 K 整除的子数组
    [leetCode]1035.不相交的线
    [leetCode]面试题 10.01. 合并排序的数组
    [leetCode]面试题04.二维数组
    [面试题03]数组中的重复数字
    [leetCode]169.多数元素
    [leetCode]160.相交链表
    [leetCode]155.最小栈
    POJ 2127 Greatest Common Increasing Subsequence
  • 原文地址:https://www.cnblogs.com/bluedoctor/p/8146604.html
Copyright © 2020-2023  润新知