两阶段提交与自杀

17. 3 月 2017 分布式 0

写在开头:以下内容基于个人对算法的理解和实操经验,可能存在漏洞,不要盲目相信……

 

在分布式系统中,为了实现事务(Transaction),通常比在单机环境中要困难许多。为了解决分布式事务的问题,两阶段提交(2PC)是一个比较传统的做法。

 

2PC的核心思想是(我认为的)分离事务的执行和提交。假设分布式系统中有1个master节点和n个slave节点。在第一阶段(prepare):

  1. master发起一个事务T,执行(但不提交,由事务隔离性对外不可见),并分发给slave。

2. slave尝试执行T(不提交),返回给master结果(成功or失败),本地写入redo和undo日志

3(a). master收到全部成功结果,发送提交T指令给slave,转到4

3(b). master收到了失败结果,发送abort T给slave,事务结束(执行失败)

4. slave尝试提交T,返回给master结果(成功or失败)

5(a). master收到全部成功结果,本地提交T,若成功则事务结束(执行成功),否则转到5(b)

5(b). master收到失败的slave结果,发送abort T给slave,事务结束(执行失败)

6. slave收到abort指令,使用undo日志回滚

大概就是如图这个样子,具体实现还可以优化的,比如在各个地方加计时器超时自动回滚等等。

看起来挺完美,但是实际上分布式环境受到网络、IO等各种因素的影响,有很多预想不到的情况出现。像这种2PC明显就有很多问题:

  • 单点故障:因为每个事务执行都是串行的,单点故障会拖慢整体效率,某些情况会使系统状态不确定。
  • 一致性:主从节点不是同时commit的,肯定会有不一致的时间存在。

 

网上有一种说法:说如果在master发出commit之后,只有一台机器(S1)收到了这个commit指令,恰巧这个时候Master和S1同时挂了,这样这个事务就进入了一种进退两难的局面:新的Master不知道这个事务应该被提交还是被取消。

网上很多人说这个很“致命”,但是实际上,新的Master可以通过重新询问slave来确定这个事务是否可以被提交(可能与最初的决策不一样,但是结果对现在的环境仍然是合法的),但是这个时间可能会很长,相当于第二阶段重新做了一遍。而所谓的“三阶段提交”我任务无非是对决策再加了一次确认,这样新的Master起来之后发现之前的确认日志,就可以断定这个事务在系统中是可以被提交的。

===========================================================

之前实习还做过这样一种方案,因为很多分布式服务都会有HA框架在的,为了提高二段提交的效率,我们给二段提交加了几个限制:

  • 多数slave提交成功,master就认为成功
  • 事务全串行,唯一递增ID,节点执行事务ID必须连续(有一个BufferQueue存放未执行事务)
  • 谁出错谁自杀(提交失败或Queue溢出)

因为自杀的成本是比较低的(HA会帮忙恢复),而且在我们的限制下,如果出现提交错误,那基本都是由IO问题引起的,而且出现的概率是非常低的。此时失败节点大概率已经出现了与主节点的不一致,所以这个时候自杀重启动是正确的选择。

但是这个feature已经在我离职之后被砍掉了

 


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.