主页

索引

模块索引

搜索页面

全局事务(Global Transactions)

  • 与本地事务相对的是全局事务,一些资料中也会称之为外部事务(External Transactions)

  • 这儿的限定定义: 一种适用于单个服务使用多个数据源场景的事务解决方案。

备注

理论上,真正的全局事务是没有 “单个服务” 这个约束的,它本来就是 DTP(Distributed Transaction Processing)模型中的概念

XA 协议

X/Open XA(XA 是 eXtended Architecture 的缩写)的事务处理框架:

核心内容是:
  定义了全局的事务管理器(Transaction Manager,用于协调全局事务)
      和局部的资源管理器(Resource Manager,用于驱动本地事务)之间的通讯接口。

XA 接口是双向的,是一个事务管理器和多个资源管理器之间通信的桥梁,
  通过协调多个数据源的动作保持一致,来实现全局事务的统一提交或者统一回滚。

备注

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 指令时,需要根据回滚日志清理已提交的数据,这可能是相对重负载操作。

警告

提到的协调者和参与者,通常都是由数据库自己来扮演的,不需要应用程序介入,应用程序相对于数据库来说只扮演客户端的角色。注意,这点是和分布式事务的区别。也可以说:全局事务是针对「分布式数据库」的,而分布式事务是的针对分布式服务的。

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)协议: 为了解决两段式提交的单点问题、性能问题和数据一致性问题

备注

新增的 CanCommit 是一个询问阶段,协调者让每个参与的数据库根据自身状态,评估该事务是否有可能顺利完成。在事务需要回滚的场景中,三段式的性能通常要比两段式好很多,但在事务能够正常提交的场景中,两段式和三段式提交的性能都很差,三段式因为多了一次询问,性能还要更差一些。

三段式提交把原本的两段式提交的准备阶段再细分为两个阶段:

CanCommit
PreCommit
DoCommit

将准备阶段一分为二的理由是,这个阶段是重负载的操作,
一旦协调者发出开始准备的消息,每个参与者都将马上开始写重做日志,这时候涉及的数据资源都会被锁住。
如果此时某一个参与者无法完成提交,相当于所有的参与者都做了一轮无用功。
https://img.zhaoweiguo.com/knowledge/images/architectures/distributes/transaction3.jpg

三段式提交

备注

因为询问阶段使得事务失败回滚的概率变小了,所以在三段式提交中,如果协调者在 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 阶段没成功,于是老师通知大家放弃此次答卷,但:
      有些同学已经涂完答题卡,并上交后离开
      另一些同学放弃这次考试,造成了不一致问题

主页

索引

模块索引

搜索页面