二阶段提交
why:
栗子:假设一个人要从 A 银行向 B 银行进行跨行转账 100 元。
希望:两个操作( -100 和 + 100),它们是一个事务, 要么同时成功, 要么同时失败 。即:原子性的提交协议(Atomic Commit Protocol)
what:
原子性提交协议(有2个特征):
安全性:任意一方 commit, 所有人必须都 commit;任意一方中断,则所有人都不 commit;
存活性:没有宕机或失败发生时, A 和 B 都能提交, 则提交;发生失败时,能够最终一致性结果(成功/失败), 予以响应, 不能一直等待;
二阶段提交协议:
上述流程的核心点:
当 TC 收到 A 和 B 响应 “Yes” 后, 做出了 “commit” 的决定, 向 A /B 发送指令, 并不需要再等待 A/B 的响应, 可以直接向 client 返回成功。
操作的保证点:当 A/B 返回 Yes 后, 就代表 A 和 B 都做好了提交准备, 只要 TC 决定要提交,即使 A/B 宕机, 没有收到 TC 的 Commit 指令, 只要 A/B 被修复重启, A 和 B 都必须有能力成功完成提交操作。
二阶段提交的安全性:
协调者 TC,作为一个中心, 统一收集了 A 和 B 是否有意愿(有能力)进行 commit;
协调者 TC 强制保证了, A, B 双方必须都有意愿提交时, 才进行 commit;
二阶段提交的存活性:
注意:协议无法满足存活性。
可能面临哪些问题:
a、响应超时。 可能原因:其他结点故障了;网络情况不好, 数据包丢失了或网络干脆中断了;
b、重启。节点宕机, 重启以后, 如何恢复被中断的操作;
等带响应超时应对:
1、TC 需要等待 A 和 B 返回 “yes”/“no” 超时:
对策:采取了非常保守的方案。
此时TC 还没有发送过任何 “commit” 指令,则此时TC可以安全地发起终止 “abort” 指令, 放弃 commit。
2、A 和 B 需要等待 TC 发送 “commit”/ “abort” 指令, 才能进行下一步操作(等待):
对策:B 为例进行考虑( A 的情形完全对称)
a、如果 B 之前回复的是 “no” , 那此时, B 直接abort。 因为 TC 即使收到了响应,也是“abort”;
b、B 之前回复了 “Yes”,则不能“abort”,也不能commit。因为A可能也yes了,并且A收到commit,只是B没收到,而应该commit;或者A回复了NO,而应该abort。
对策:B针对这种情形发起一轮终止协议操作(Termination Protocol)
超时终止协议:
B向A咨询
B没有响应:则B只能等待(TC恢复,再查询TC);
A收到“commit”/ “abort” 指令:B跟随;
A没回复 “yes”/“no”:A、B直接abort。(TC超时后,也会下发abort);
A回复NO:A、B直接abort;
A回复yes:B则只能等TC信(TC恢复,再查询TC);(原因:A 和 B 可能都 “Yes”,只是A、B没收到,但是也就OK于client;TC可能等待超时,只是abort没到 )
宕机重启应对:
基本原则: 一旦 TC 决定了 commit , 那么任意一个结点都不允许发生回滚。
方案:所有的结点, 都能知道他们在宕机前的状态是什么, 那就有如下几种解决方案:
a、A 或 B 相互发起之前描述的终止协议 Termination Protocol, 即相互询问是否知道事务已经交;
b、A 和 B 也可以向 TC 发起状态查询操作, TC 可能知道事务是否已经提交;
具体措施:所有节点发送任何信息给其他结点前, 一定要先行将自己要回复的内容写入磁盘。
TC 而言, 在向 A 和 B 发送 “commit” 指令前, 一定要先行将 “commit” 成功记录到磁盘;
A/B 而言, 在向 TC 发送 “yes” 之前, 一定要先行将 “yes” 记录成功记录到磁盘;
重启之后就可以进行如下的操作:
a、对于TC, 重启以后, 如果发现磁盘中没有记录 “commit” , 那就可以直接进行 “abort” 操作;
b、 A 或者 B , 重启以后, 如果发现磁盘中没有记录 “yes” , 那就可以直接进行 “abort” 操作;
c、A或者 B, 重启以后, 如果发现磁盘中有 “yes” 记录, 那就可以发起终止协议 “termination protocol”;
d、TC, A, B 都发生了重启操作, 只要当 3 个结点都恢复以后, 就可以向 TC 发起查询, 查看TC 的磁盘中是否存在 “commit” 记录, 如果存在, 则均可进行 “commit” 操作。