亿级流量架构之分布式事务思路及方法( 四 )


第一阶段:投票该阶段的主要目的在于打探数据库集群中的各个参与者是否能够正常的执行事务 , 具体步骤如下:

  1. 协调者向所有的参与者发送事务执行请求 , 并等待参与者反馈事务执行结果;
  2. 事务参与者收到请求之后 , 执行事务但不提交 , 并记录事务日志;
  3. 参与者将自己事务执行情况反馈给协调者 , 同时阻塞等待协调者的后续指令 。
第二阶段:事务提交在经过第一阶段协调者的询盘之后 , 各个参与者会回复自己事务的执行情况 , 这时候存在 3 种可能性:
  1. 所有的参与者都回复能够正常执行事务 。
  2. 一个或多个参与者回复事务执行失败 。
  3. 协调者等待超时 。
对于第 1 种情况 , 协调者将向所有的参与者发出提交事务的通知 , 具体步骤如下:
  1. 协调者向各个参与者发送 commit 通知 , 请求提交事务;
  2. 参与者收到事务提交通知之后执行 commit 操作 , 然后释放占有的资源;
  3. 参与者向协调者返回事务 commit 结果信息 。

亿级流量架构之分布式事务思路及方法

文章插图
 
对于第 2 和第 3 种情况 , 协调者均认为参与者无法成功执行事务 , 为了整个集群数据的一致性 , 所以要向各个参与者发送事务回滚通知 , 具体步骤如下:
  1. 协调者向各个参与者发送事务 rollback 通知 , 请求回滚事务;
  2. 参与者收到事务回滚通知之后执行 rollback 操作 , 然后释放占有的资源;
  3. 参与者向协调者返回事务 rollback 结果信息 。

亿级流量架构之分布式事务思路及方法

文章插图
 
两阶段提交协议解决的是分布式数据库数据强一致性问题 , 实际应用中更多的是用来解决事务操作的原子性 , 下图描绘了协调者与参与者的状态转换 。
亿级流量架构之分布式事务思路及方法

文章插图
 
站在协调者的角度 , 在发起投票之后就进入了 WAIT 等待状态 , 等待所有参与者回复各自事务执行状态 , 并在收到所有参与者的回复后决策下一步是发送 commit提交 或 rollback回滚信息 。
站在参与者的角度 , 当回复完协调者的投票请求之后便进入 READY 状态(能够正常执行事务) , 接下去就是等待协调者最终的决策通知 , 一旦收到通知便可依据决策执行 commit 或 rollback 操作 。
两阶段提交协议原理简单、易于实现 , 但是缺点也是显而易见的 , 包含如下:
  • 单点问题
协调者在整个两阶段提交过程中扮演着举足轻重的作用 , 一旦协调者所在服务器宕机 , 就会影响整个数据库集群的正常运行 。比如在第二阶段中 , 如果协调者因为故障不能正常发送事务提交或回滚通知 , 那么参与者们将一直处于阻塞状态 , 整个数据库集群将无法提供服务 。
  • 同步阻塞
两阶段提交执行过程中 , 所有的参与者都需要听从协调者的统一调度 , 期间处于阻塞状态而不能从事其他操作 , 这样效率极其低下 。
  • 数据不一致性
两阶段提交协议虽然是分布式数据强一致性所设计 , 但仍然存在数据不一致性的可能性 。比如在第二阶段中 , 假设协调者发出了事务 commit 通知 , 但是因为网络问题该通知仅被一部分参与者所收到并执行了commit 操作 , 其余的参与者则因为没有收到通知一直处于阻塞状态 , 这时候就产生了数据的不一致性 。
针对上述问题可以引入 超时机制 和 互询机制在很大程度上予以解决 。
超时机制对于协调者来说如果在指定时间内没有收到所有参与者的应答 , 则可以自动退出 WAIT 状态 , 并向所有参与者发送 rollback 通知 。对于参与者来说如果位于 READY 状态 , 但是在指定时间内没有收到协调者的第二阶段通知 , 则不能武断地执行 rollback 操作 , 因为协调者可能发送的是 commit 通知 , 这个时候执行 rollback 就会导致数据不一致 。
互询机制此时 , 我们可以介入互询机制 , 让参与者 A 去询问其他参与者 B 的执行情况 。如果 B 执行了 rollback 或 commit 操作 , 则 A 可以大胆的与 B 执行相同的操作;如果 B 此时还没有到达 READY 状态 , 则可以推断出协调者发出的肯定是 rollback 通知;如果 B 同样位于 READY 状态 , 则 A 可以继续询问另外的参与者 。只有当所有的参与者都位于 READY 状态时 , 此时两阶段提交协议无法处理 , 将陷入长时间的阻塞状态 。


推荐阅读