分布式事务-02-两阶段提交

理解两阶段提交,需要首先了解 XA 协议。

XA定义:XA是由X/Open组织提出的分布式事务的规范。主要定义了**(全局)事务管理器TM(局部)资源管理器(RM)**之间的接口,主流的关系型 数据库产品都是实现了XA接口的

两阶段提交(Two-phase Commit Protocol,简称 2PC)针对的是刚性事务,将一个分布式的事务过程拆分成两个阶段: 投票事务提交 ,是一个非常经典的强一致、中心化的原子提交协议

流程

AP 向 TM 提交请求,发起分布式事务

第一阶段 准备阶段(Prepare Phase)

img

准备阶段,主要目的在于打探数据库集群中的各个 RM 是否能够正常的执行事务,具体步骤如下:

  1. TM 向所有的 RM 发送事务预处理请求 REQUEST-TO-PREPARE,并等待 RM 反馈事务执行结果(准备结果)
  2. RM 收到请求之后,执行询问发起为止的所有事务操作,并将 Undo 信息和 Redo 信息写入日志
  3. RM 响应 TM 发起的询问。如果 RM 第二步 执行成功,则返回PREPARED;否则返回 NO。同时阻塞等待 TM 的后续指令

第一阶段准备阶段也被称作投票阶段,即各 RM 投票是否要继续接下来的提交操作

这里就会存在一些问题:

  1. TM 发送请求部分 RM 没有收到

    答:RM 会阻塞等待,而 TM 也会因为收不到回复而阻塞等待。解决方案,提供超时机制, TM超时回滚或咨询事务执行结果逻辑

  2. RM 收到请求是如何准备的

    答:特殊处理机制,看下面的 MySQL 执行 XA 命令

  3. RM 收到请求之后执行了准备操作但是没有响应

    答:TM 会阻塞,解决方案,提供超时机制, TM超时回滚或咨询事务执行结果逻辑

  4. TM 等待响应部分或全部超时如何处理

    答:TM 会阻塞,解决方案,提供超时机制, TM超时回滚或咨询事务执行结果逻辑

可惜的是,当 TM 无法收到所有 RM 的回复的时候,TM 会陷入阻塞!!需要超时逻辑进行下一步处理

在具体应用中超时/失败场景可以根据实际的系统需要而进行方案设计,这里的协议仅仅做了流程上的考量

  • 超时重试:TM 可以尝试重新发送准备请求给失败的 RM,设定一个合理的超时时间。如果在超时时间内收到了 RM 的准备就绪通知,那么可以继续进行提交阶段。如果超时后仍未收到响应,可以将 RM 标记为失败,并回滚事务。
  • 回滚事务:如果准备请求发送失败,可以将事务标记为回滚,并通知所有 RM 回滚事务。这样可以确保所有 RM 处于一致的状态,即使其中一部分 RM 未能接收到准备请求。
  • 异常处理:如果准备请求的发送失败是由于网络故障、RM 崩溃或其他不可预见的错误引起的,可以捕获异常并进行相应的处理。可以根据具体的系统需求,选择适当的策略,如重试、回滚或者向管理员报告问题

RM 支持 XA 协议,在收到 TM 请求之后,可以根据 MySQL 命令看看 RM 是如何处理的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 第一阶段
// 1.1 通过 XA START 和 XA END 来包裹 用户业务SQL
mysql> XA START 'transfer_money';
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE account SET money = money -100 where id = 1 ;
Query OK, 1 row affected (0.00 sec)

mysql> XA END 'transfer_money';
Query OK, 0 rows affected (0.00 sec)

// 1.2 通过 XA PREPARE 通知 RM 一阶段就绪
mysql> XA PREPARE 'transfer_money';
Query OK, 0 rows affected (0.00 sec)

// 第二阶段,通过 XA COMMIT 完成二阶段的提交
mysql>
mysql> XA COMMIT 'transfer_money';
Query OK, 0 rows affected (0.00 sec)

第二阶段 提交阶段(Commit Phase)

在第一阶段 TM 收到所有 RM 返回成功的情况下,流程如下所示

img
  • 如果所有的 RM 都回复的是PREPARED, 那么 TM 向所有 RM 发送COMMIT 消息;

  • 否则 TM 向所有回复PREPARED的 RM 发送ABORT消息;

  • RM 如果收到 TM 发来的COMMIT消息则提交,ABORT消息则回滚,并向 TM 发送DONE消息以确认

不懂就问:

  1. 部分 RM 并没有收到 TM 的提交或者回滚、

    答:数据可能出现不一致。解决方案,可以引入咨询机制查看事务是否正常,阻塞时间更长

  2. TM 等待 RM 的提交响应超时如何处理。

    答:TM 无法知道执行结果,可能出现不一致,解决方案,可以引入咨询机制查看事务是否正常,阻塞时间更长

  3. TM 作为事务协调者,是否存在单点故障以及性能瓶颈。

    答:可以通过协商选举一个出来(如果能保存未提交事务让备TM 继续咨询协调更好)

当TM 等待响应部分超时或者失败,TM 均认为 RM 无法成功执行事务,为了整个集群数据的一致性,向各个 RM 发送事务回滚通知:

  1. TM 向各个 RM 发送事务 rollback 通知,请求回滚事务
  2. RM 收到事务回滚通知之后执行 rollback 操作,然后释放占有的资源
  3. RM 向 TM 返回事务 rollback 结果信息(发送也可能失败)

不懂就问:

  1. 部分RM 并没有收到回滚通知怎么办?

    答:所有处于执行了操作但是未提交状态的 RM 都会陷入阻塞情况.

总结

优点:

  1. 两阶段提交支持不同数据库的分布式事务,比如一个是 MySQL,另外一个是 Oracle
  2. 业务无侵入:XA 模式将是业务无侵入的,不给应用设计和开发带来额外负担
  3. 数据库的支持广泛:XA 协议被主流关系型数据库广泛支持,不需要额外的适配即可使用

缺点:

  1. 同步阻塞,无论是在第一阶段的过程中,还是在第二阶段,所有 RM 资源和 TM 资源都是被锁住的,只有当所有 RM 准备完毕,TM 才会通知进行全局提交,RM 进行本地事务提交后才会释放资源。这样的过程会比较漫长,对性能影响比较大
  2. 单点故障,一旦 TM 发生故障。RM 会一直阻塞下去。尤其在第二阶段,TM 发生故障,那么所有的 RM 还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是 TM 挂掉,可以重新选举一个TM ,但是无法解决因为 TM 宕机导致的 RM 处于阻塞状态的问题)
  3. 数据不一致。当只有部分 RM 收到commit请求,会导致整个分布式系统便出现了数据部一致性的现象。
  4. 脑裂,当 TM 发出 commit 消息之后宕机,而接收到这条消息的 RM 同时也宕机了。那么即使 TM 通过选举协议产生了新的TM ,这条事务的状态也是不确定的,没人知道事务是否被已经提交

参考文档

  1. https://pdai.tech/md/arch/arch-z-transection.html#分布式事务方案之刚性事务
  2. https://zh.wikipedia.org/wiki/二阶段提交
  3. https://blog.51cto.com/u_15287666/2989395
  4. https://www.cnblogs.com/qdhxhz/p/11167025.html