问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

两阶段提交(2PC)原理分析

创作时间:
作者:
@小白创作中心

两阶段提交(2PC)原理分析

引用
51CTO
1.
https://blog.51cto.com/u_16185072/11016561

两阶段提交(2PC)是一种用于分布式系统中事务处理的算法,确保所有节点在提交事务时保持一致性。本文将详细介绍2PC的原理、流程、事务日志处理以及故障处理策略。

协议整体流程

上图展示了2PC的基本流程,其中Database A和Database C是两个参与者(worker/participant/cohort),通常是关系数据库。协调者(coordinator/transaction manager)一般位于应用服务器(如Java EE容器)中,也可以单独部署。2PC的执行过程可以分为三个阶段:

  1. 发送SQL语句阶段:在客户端开始分布式事务时,coordinator先生成一个事务ID,然后在每个worker上开启事务。客户端通过coordinator向每个worker发送多个读写SQL。在悲观串行化隔离级别下,worker需要用读锁和写锁分别锁定所有被读取和写入的数据。

  2. 准备(Prepare)阶段:coordinator分别给各个worker发送询问确认单节点内的事务是否可以保证被提交。如有任何一个节点返回no,则回滚(abort)所有节点的事务。

  3. 提交(Commit)阶段:coordinator给每个worker发送提交事务的指令,最终提交本地的事务并释放资源相关的锁。

事务日志处理

上图展示了事务日志的处理流程。为了确保事务的可靠性和故障恢复能力,coordinator和worker需要在关键进度点记录日志。具体来说:

  • coordinator的日志记录

  • 在prepare阶段开始时记录BEGIN日志,包含事务ID和涉及的worker。

  • prepare全部确认后记录COMMITTED日志。一旦记录COMMITTED日志,意味着这个事务一定会被提交。

  • worker的日志记录

  • 确认事务可以提交后记录BEGIN和PREPARED日志。例如,MySQL使用redo log来记录prepare。

  • 在自己提交后记录COMMITTED日志。例如,MySQL使用binlog来记录COMMITTED日志。

故障点及处理

以下是可能的故障处理策略:

  • 故障发生在发送SQL语句阶段:如果任何节点发生故障,可以直接给客户端返回错误。有些开始了事务的worker,coordinator可以将其回滚。如果coordinator挂了,则worker需要自己等待事务超时后回滚。

  • 故障发生在prepare阶段

  • 情况1:worker发生了故障。coordinator需要不停地重试,直到worker恢复后给出响应。因为coordinator无法确认故障worker是否已经进入了prepared阶段,所以不能直接回滚整体事务。

  • 情况2:coordinator发生了故障。coordinator在恢复后会从日志中读取到该已经BEGIN但未COMMITTED的事务,然后重新对每个worker发起prepare操作。worker需要保证prepare操作是幂等的。

  • 故障发生在commit阶段

  • 情况1:worker发生了故障。coordinator需要重试,直到worker恢复后给出响应。因为worker在prepare阶段记录了事务细节并锁定了临界资源,所以能保证宕机恢复后事务依然一定可以提交成功。

  • 情况2:coordinator发生了故障。coordinator在恢复后会从日志中读取到该已经COMMITTED但未被删除的事务记录,然后重新对每个worker发起commit操作。worker需要保证commit操作是幂等的。

2PC的问题

  1. 长久锁定的问题(blocking problem):在prepare之后每个worker需要继续锁定相关资源,一直到收到coordinator的commit指令。如果coordinator在prepare之后commit之前挂了,会导致临界资源在coordinator恢复前会一直处于锁定状态。

  2. 性能问题:多次远程通讯、多次日志写入、锁定数据等均会降低性能。据测试,MySQL中的分布式事务比单节点事务慢10倍以上,TPS参考值为几千。

  3. 部署问题:如果将coordinator与应用服务器部署到一起,因为coordinator需要存储日志,会将应用服务器变为有状态的服务,会降低其运维的灵活性。如果独立部署coordinator又会增加一层远程调用。

  4. 死锁问题:coordinator无法检测跨多个worker的死锁。

  5. 脏读的问题:2PC因提交阶段是无法保证所有worker都在同一个时间点commit的,所以先被commit的资源会先被读取到。

思考

  1. 为什么要prepare阶段?:prepare阶段worker会将事务进度持久化,从而保证worker宕机恢复后可以继续提交事务。prepare返回true代表worker承诺事务一定能提交。

  2. 有没有可能去掉prepare阶段呢?:是可能的。但需要支持commit之后的逆向操作(undone, compensating transaction),且在整体事务完成前要保持对资源的锁定,这导致最终还是需要一次确认操作来释放锁。这样跟加入prepare阶段比并不能带来优势。

参考资料

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号