全局事务(Global Transactions) ############################# * 与本地事务相对的是全局事务,一些资料中也会称之为外部事务(External Transactions) * 这儿的限定定义: 一种适用于单个服务使用多个数据源场景的事务解决方案。 .. note:: 理论上,真正的全局事务是没有 “单个服务” 这个约束的,它本来就是 DTP(Distributed Transaction Processing)模型中的概念 XA 协议 ======= X/Open XA(XA 是 eXtended Architecture 的缩写)的事务处理框架:: 核心内容是: 定义了全局的事务管理器(Transaction Manager,用于协调全局事务) 和局部的资源管理器(Resource Manager,用于驱动本地事务)之间的通讯接口。 XA 接口是双向的,是一个事务管理器和多个资源管理器之间通信的桥梁, 通过协调多个数据源的动作保持一致,来实现全局事务的统一提交或者统一回滚。 .. note:: XA 协议是主要从数据库本身的协同工作出发的。 内部 XA:: 实例:MySQL 就支持多个数据库引擎之间通过 XA 组成全局事务, 直接向 MySQL 发送 XA START、XA END 、XA PREPARE/COMMIT/ROLLBACK 等指令就可以驱动事务运行。 外部 XA:: 由各种异构数据源(不同数据库、消息列、缓存、etc,反正只要支持 XA 协议)来组成的 XA 事务, 通过其上的应用层扮演协调者也是普遍行为, 此时协调者与客户端在逻辑上仍是独立的角色,但物理上完全有可能是同一台应用服务器去扮演 两段式提交 ========== * “准备” 和 “提交” 这两个过程,被称为 `“两段式提交” `_ (2 Phase Commit,2PC)协议 1. 准备阶段:: 又叫做投票阶段。 在这一阶段,协调者询问事务的所有参与者是否准备好提交, 如果已经准备好提交回复 Prepared,否则回复 Non-Prepared。 准备阶段结束代表写完日志。 2. 提交阶段:: 又叫做执行阶段 协调者如果在准备阶段收到所有事务参与者回复的 Prepared 消息,就会首先在本地持久化事务状态为 Commit, 然后向所有参与者发送 Commit 指令,所有参与者立即执行提交操作; 否则,任意一个参与者回复了 Non-Prepared 消息,或任意一个参与者超时未回复, 协调者都会将自己的事务状态持久化为 “Abort” 之后,向所有参与者发送 Abort 指令,参与者立即执行回滚操作。 我的理解:: 准备阶段: 也可能持久化了操作日志 提交阶段: 「参与者」只是在本地持久化了一条「Commit Record」日志 所以很轻量,剩下的工作可以通过「Commit Log」来实现 当然如果失败回滚,这块就变成一个重负载操作了 两阶段提交协议,保证一致性两个前提条件:: 1. 必须假设网络在提交阶段这个短时间内是可靠的,即提交阶段不会丢失消息 两段式提交中投票阶段失败了可以补救(回滚), 而提交阶段失败了无法补救(不再改变提交或回滚的结果,只能等崩溃的节点重新恢复), 因而提交阶段的耗时应尽可能短,这也是为了尽量控制网络风险的考虑。 2. 必须假设因为网络分区、机器崩溃或者其他原因而导致失联的节点最终能够恢复,不会永久性地处于失联状态 由于在准备阶段已经写入了完整的重做日志, 所以当失联机器一旦恢复,就能够从日志中找出已准备妥当但并未提交的事务数据, 再向协调者查询该事务的状态,确定下一步应该进行提交还是回滚操作。 数据库来说 提交阶段: 相对轻量,仅仅是持久化一条 Commit Record 而已,通常能够快速完成。 回滚阶段: 相对耗时,收到 Abort 指令时,需要根据回滚日志清理已提交的数据,这可能是相对重负载操作。 .. warning:: 提到的协调者和参与者,通常都是由数据库自己来扮演的,不需要应用程序介入,应用程序相对于数据库来说只扮演客户端的角色。注意,这点是和分布式事务的区别。也可以说:全局事务是针对「分布式数据库」的,而分布式事务是的针对分布式服务的。 .. figure:: https://img.zhaoweiguo.com/knowledge/images/architectures/distributes/transaction2.jpg 两段式提交 两段式提交的原理很简单,也不难实现,但有三个非常明显的缺点:: 1. 单点问题 协调者在两段提交中具有举足轻重的作用 协调者等待参与者回复时可以有超时机制,允许参与者宕机,但参与者等待协调者指令时无法做超时处理。 一旦协调者宕机,所有参与者都会受到影响。 如果协调者一直没有恢复,没有正常发送 Commit 或者 Rollback 的指令,那所有参与者都必须一直等待。 2. 性能问题 两段提交过程中,所有参与者相当于被绑定成为一个统一调度的整体 期间要经过两次远程服务调用、三次数据持久化 (准备阶段写重做日志,协调者做状态持久化,提交阶段在日志写入 Commit Record) 整个过程将持续到参与者集群中最慢的那一个处理操作结束为止。 这就决定了两段式提交的性能通常都比较差。 3. 一致性风险 当网络稳定性和宕机恢复能力的假设不成立时,两段式提交可能会出现一致性问题。 “单点问题” 中为啥 “参与者等待协调者指令时无法做超时处理”:: 因为准备阶段结束,所有参与者都回复了 Prepared 的那一刻起,整个集群的数据状态就无法再改变了。 你可以想像一下这个情景: 假设某个参与者在应答 Prepared 消息后从网络中断开,丢失了协调者的后续指令, 如果允许超时,超时之后它应该如何处理呢? 如果它选择 Rollback 了数据,万一协调者收到所有 Prepared 消息后, 发出的是 Commit 指令,那其他参与者会提交事务,这样该节点超时后重新上线, 它后超时回滚掉那部分数据就与其他参与者的不一致了。 反之,超时提交也是不合适的,因为怕协调者发出的是 Abort 指令。 三段式提交 ========== * `“三段式提交” `_ (3 Phase Commit,3PC)协议: 为了解决两段式提交的单点问题、性能问题和数据一致性问题 .. note:: 新增的 CanCommit 是一个询问阶段,协调者让每个参与的数据库根据自身状态,评估该事务是否有可能顺利完成。在事务需要回滚的场景中,三段式的性能通常要比两段式好很多,但在事务能够正常提交的场景中,两段式和三段式提交的性能都很差,三段式因为多了一次询问,性能还要更差一些。 三段式提交把原本的两段式提交的准备阶段再细分为两个阶段:: CanCommit PreCommit DoCommit 将准备阶段一分为二的理由是,这个阶段是重负载的操作, 一旦协调者发出开始准备的消息,每个参与者都将马上开始写重做日志,这时候涉及的数据资源都会被锁住。 如果此时某一个参与者无法完成提交,相当于所有的参与者都做了一轮无用功。 .. figure:: https://img.zhaoweiguo.com/knowledge/images/architectures/distributes/transaction3.jpg 三段式提交 .. note:: 因为询问阶段使得事务失败回滚的概率变小了,所以在三段式提交中,如果协调者在 PreCommit 阶段开始之后发生了宕机,参与者没有能等到 DoCommit 的消息的话,默认的操作策略将是提交事务而不是回滚事务或者持续等待。这就相当于避免了协调者的单点问题,但同时也引入了一致性问题(详见上图)。 三段式提交对单点问题和回滚时的性能问题有所改善,但是对一致性风险问题并未有任何改进,甚至是增加了面临的一致性风险:: 进入 PreCommit 阶段之后,协调者在DoCommit阶段发出指令 Abort, 而此时因为网络问题,有部分参与者直至超时都没能收到协调者的 Abort 指令的话, 这些参与者将会错误地提交事务,这就产生了不同参与者之间数据不一致的问题。 趣味讲二/三段式提交 =================== 题目:: 学校组织知识竞赛,全部学生(参与者)以一组为单位参加比赛,由一个监考老师(协调者)负责监考。 考试分为考卷和答题卡,学生必须先在十分钟内把答案写在考卷上(记录日志), 然后把答案涂到答题卡上(提交事务) 趣味讲二段式提交:: 1. 准备阶段 老师宣布:“开始填写考卷,时间十分钟”。 十分钟内,写好考卷的学生就回答:Prepared 十分钟一到,动作慢,还没写好学生,就回答:Non-Prepared 中间有同学不舒服,可以直接回答:Non-Prepared 2. 提交阶段 1.1 全部学生都回答 Prepared 老师就会在笔记本上记下,“开始填答题卡”(Commit) 然后对所有的学生说:“开始填答题卡”(发送 Commit 指令) 学生听到指令后,就开始根据考卷去涂答题卡: 2.1 所有学生涂完答题卡并都通知老师 老师收到所有人涂完答题卡的回答,事务完成 2.2 3分钟后老师还没有收到涂完的回答 老师会再次通知学生去涂答题卡 如果这时老师有事出去,所有同学都会停下来等着老师回来 因为不知道老师回来后会让大家涂答题卡还是放弃这次考试 1.2 如果「准备阶段」有学生回答 Non-Prepared 老师通知所有学生放弃此次答卷 并把答卷用涂改液涂成空白状态 趣味讲三段式提交:: 1. CanCommit 阶段 老师先给学生看一下考卷,问问学生能不能在十分钟内做完。 如果有学生说没信心做完,老师通知所有学生放弃此次答卷。 2. PreCommit 阶段 如果学生都说能做完,老师就宣布:“开始填写考卷,时间十分钟”,和两段式提交的准备阶段一样 3. DoCommit 阶段 和两段式提交的提交阶段基本一样 不同的点在,如果这时老师有事出去,所以没有通知同学去涂答题卡 用户在完成PreCommit 阶段后3分钟自己去涂答题卡 如果老师5分钟后回来,发现有个在PreCommit 阶段没成功,于是老师通知大家放弃此次答卷,但: 有些同学已经涂完答题卡,并上交后离开 另一些同学放弃这次考试,造成了不一致问题