【分布式】两阶段提交

149 阅读3分钟

这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战.


参考:《数据库系统概念》19.3,19.4

概念

分布式事务由一个站点发起,多个站点参与。事务必须保证要么在所有站点上都提交,要么在所有站点上都中止。

两阶段提交Two-Phase Commit,2PC)协议是最简单且使用最广泛的提交协议之一。

考虑一个例子,站点 S0S_0 发起了事务 TT,用 SiS_i 来表示各个参与执行事务的站点。

提交协议

当所有 SiS_i 都通知 S0S_0 完成了 TT 的执行时,S0S_0 启动 2PC2PC 协议。

阶段1

  1. S0S_0 在稳定存储器上记录日志 <prepare T>,然后给 SiS_i 发送消息 prepare T.
  2. SiS_i 收到消息,确认是否愿意提交。
    1. 如果选择不提交,就记录日志 <abort T>,然后向 S0S_0 发送消息 abort T.
    2. 如果选择提交,就记录日志 <ready T>,并将所有与 TT 相关的日志写入稳定存储器中,然后向 S0S_0 发送消息 ready T.

阶段2

  1. 如果 S0S_0 收到所有 SiS_iready T 消息,就将日志 <commit T> 写入稳定存储器中,然后向 SiS_i 发送消息 commit T.
  2. 如果 S0S_0 收到至少一个 abort T 或者间隔一段时间后没有收到全部消息,就将日志 <abort T> 写入稳定存储器中,然后向 SiS_i 发送消息 abort T.
  3. SiS_i 收到消息后,将消息记录到日志中。

故障处理

S0S_0SiS_i 发生故障有不同的处理方式。

某个站点 SiS_i 故障

S0S_0 的行动

  1. 如果 S0S_0SiS_i 发送 ready T 之前发现 SiS_i 故障,就假定它发送了 abort T.
  2. 如果 S0S_0SiS_i 发送 ready T 之后发现 SiS_i 故障,就忽略它的故障.

SiS_i 恢复措施

SiS_i 从故障中恢复时,检查它的日志以判断应该做什么。

  1. 日志中存在 <commit T>,此时应执行 redo(T)redo(T).
  2. 日志中存在<abort T>,此时应执行 undo(T)undo(T).
  3. 日志中仅存在 <ready T>,此时 SiS_iTT 称作疑问事务,应当向 S0S_0 或者其它 SiS_i 询问最终的处理结果。
  4. 日志中不存在上述三者,此时应执行 undo(T)undo(T)。这说明 S0S_0 没有收到来自 SiS_i 的回复。

对疑问事务的处理可能导致一直询问且影响其它事务进行,一个简单的改进是,SiS_i 在记录 <ready T> 日志时,将 TT 所使用的锁记录下来。等站点恢复后,将这些锁重新申请,这样就不会阻塞与这些锁无关的事务。

S0S_0 故障

S0S_0 发生故障时,SiS_i 通过检查自己或其它 SiS_i 的日志以做出决定。

  1. 日志中存在 <commit T>,此时应执行 redo(T)redo(T).
  2. 日志中存在 <abort T>,此时应执行 undo(T)undo(T).
  3. 所有 SiS_i 的日志中都是仅存在 <ready T>,此时所有 SiS_i 必须等待 S0S_0 恢复后才能得到答案。如果 TT 持有 SiS_i 上的锁,那么 SiS_i 的其它事务也会收到影响,称作阻塞(blocking)
  4. 某些 SiS_i 的日志不存在 <ready T>,说明它们没有向 S0S_0 发送过 ready T,此时直接执行 undo(T)undo(T).

评价

2PC2PC 的主要缺陷在于协调器故障可能会导致阻塞。

感想

概念基本理解了,想找个成型的代码实例学习一下。


本文也发表于我的 csdn 博客中。