二阶段提交
两阶段提交协议的目标在于为分布式系统保证数据的一致性,顾名思义,该协议将一个分布式的事务过程拆分成两个阶段: 投票 和 事务 提交 ,为了让整个数据库集群能够正常的运行,该协议指定了一个 协调者 单点,用于协调整个数据库集群各节点的运行
第一阶段:投票
- 协调者向所有的参与者发送事务执行请求,并等待参与者反馈事务执行结果;
- 事务参与者收到请求之后,执行事务但不提交,并记录事务日志;
- 参与者将自己事务执行情况反馈给协调者,同时阻塞等待协调者的后续指令
第二阶段:事务提交
- 协调者向各个参与者发送 commit 通知,请求提交事务;
- 参与者收到事务提交通知之后执行 commit 操作,然后释放占有的资源;
- 参与者向协调者返回事务 commit 结果信息。
执行成功的情况
执行失败的情况
so,二阶段提交的整体流程图是这样的
二阶段提交出现的问题
两阶段提交协议原理简单、易于实现,但是缺点也是显而易见的,主要以下2个问题
1. 单点故障问题
协调者在整个两阶段提交过程中扮演着非常重要的角色,一旦协调者所在服务器宕机,就会影响整个数据库集群的正常运行。比如在第二阶段中,如果协调者因为故障不能正常发送事务提交或回滚通知,那么参与者们将一直处于阻塞状态,整个数据库集群将无法提供服务。
2. 同步阻塞
两阶段提交执行过程中,所有的参与者都需要听从协调者的统一调度,期间若协调者处于阻塞状态而不能从事其他操作,这样效率极其低下。
3. 数据不一致性
两阶段提交协议虽然是解决了分布式一致性,但仍然存在数据不一致性的可能性。比如在第二阶段中,假设协调者发出了事务 commit 通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了commit 操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。
针对上述问题可以引入 超时机制 和 互询机制 在很大程度上予以解决。
三阶段提交
针对两阶段提交存在的问题,三阶段提交协议通过引入一个 准备
阶段,以及 超时策略
来减少集群的阻塞时间,提升系统性能;三个杰顿分别为:
- 第一阶段:准备
- 第二阶段:预提交
- 第三阶段:事务提交
第一阶段:准备
该阶段协调者会去询问各个参与者是否能够正常执行事务,参与者根据自身情况回复,具体步骤如下:
- 协调者向各个参与者发送事务询问通知,询问是否可以执行事务操作,并等待回复;
- 各个参与者依据自身状况回复一个预估值,如果预估自己能够正常执行事务就返回确定信息,并进入预备状态,否则返回否定信息
第二阶段:预提交
在预提交阶段下,协调者会根据第一阶段的询盘结果采取相应操作,询盘结果主要有 3 种:
- 所有的参与者都返回确定信息。
- 一个或多个参与者返回否定信息。
- 协调者等待超时
针对第 1 种情况,协调者会向所有参与者发送事务执行请求,具体步骤如下:
- 协调者向所有的事务参与者发送事务执行通知;
- 参与者收到通知后执行事务但不提交;
- 参与者将事务执行情况返回给客户端。
在上述步骤中,如果参与者等待超时,则会中断事务 ,
针对第 2 和第 3 种情况,协调者认为事务无法正常执行,于是向各个参与者发出 abort 通知,请求退出预备状态,具体步骤如下:
- 协调者向所有事务参与者发送 abort 通知;
- 参与者收到通知后中断事务。
第三阶段:事务提交
如果第二阶段事务未中断,那么本阶段协调者将会依据事务执行返回的结果来决定提交或回滚事务,分为 3 种情况:
- 所有的参与者都能正常执行事务。
- 一个或多个参与者执行事务失败。
- 协调者等待超时。
针对第 1 种情况,协调者向各个参与者发起事务提交请求,具体步骤如下:
- 协调者向所有参与者发送事务 commit 通知;
- 所有参与者在收到通知之后执行 commit 操作,并释放占有的资源;
- 参与者向协调者反馈事务提交结果。
针对第 2 和第 3 种情况,协调者认为事务无法成功执行,于是向各个参与者发送事务回滚请求,具体步骤如下:
- 协调者向所有参与者发送事务 rollback 通知;
- 所有参与者在收到通知之后执行 rollback 操作,并释放占有的资源;
- 参与者向协调者反馈事务回滚结果。
超时策略
在第三阶段如果因为协调者或网络问题,导致参与者迟迟不能收到来自协调者的 commit 或 rollback 请求,那么参与者将不会如两阶段提交中那样陷入阻塞,而是等待超时后继续 commit,相对于两阶段提交虽然降低了同步阻塞,但仍然无法完全避免数据的不一致。
三阶段提交整体流程图如下
结语
两阶段提交协议中所存在的长时间阻塞状态发生的几率还是非常低的,所以虽然三阶段提交协议相对于两阶段提交协议对于数据强一致性更有保障,但是因为效率问题,两阶段提交协议在实际系统中反而更加受宠。