一、分布式一致性概念
1. 协调者统一调度
在分布式系统中,各个节点之间在物理上相互独立,通过网络进行沟通和协调。在关系型数据库中,由于存在事务机制,可以保证每个独立节点上的数据操作满足ACID。但是,相互独立的节点之间无法准确的知道其他节点中的事务执行情况,所以在分布式的场景下,如果不添加额外的机制,多个节点之间理论上无法达到一致的状态。
2PC 和 3PC 提交协议都是引入了一个“协调者 [Coordinator]”的组件来统一调度所有分布式节点的执行(这些被调度的节点被称为“参与者 [Participants]”),让当前节点知道其他节点的任务执行状态,通过通知和表决的方式,决定执行 Commit 还是 Rollback 操作。
2. 一致性算法
两类一致性算法:
2PC 3PC:操作原子性
协议用于保证属于多个数据分片上的操作的原子性。这些数据分片可能分布在不同的服务器上,2PC协议保证多台服务器上的操作要么全部成功,要么全部失败。
Paxos Raft Zab:副本一致性
协议用于保证同一个数据分片的多个副本之间的数据一致性。当这些副本分布到不同的数据中心时,这个需求尤其强烈。
在分布式事务中,2PC 和 3PC 是经典的一致性算法,那么 2PC 和 3PC 的具体流程是怎样的,3PC 又是如何改进的呢?
二、2PC(2 Phase Commit)
两阶段提交中的两个阶段,指的是 Commit-request 阶段和 Commit 阶段,两阶段提交的流程如下:
1. 协议说明
第一阶段:提交事务请求
-
事务询问
协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者的响应。 -
执行事务
各参与者节点执行事务操作,并将 Undo 和 Redo 信息计入事务日志中。 -
反馈响应
如果本地事务执行成功,反馈给协调者 Yes;
如果本地事务执行故障,反馈给协调者 No。
第二阶段:执行事务提交
如果把阶段一比喻成投票阶段,那么在阶段二中,就会根据阶段一的投票结果来决定最终是否可以进行事务提交操作。正常情况下,包含两种操作可能:提交事务、中断事务。
-
提交事务
如果所有参与者的反馈都是 Yes 响应,那么就会执行事务提交:- 发送提交请求
协调者向所有参与者节点发出 Commit 请求 - 事务提交
参与者接收到 Commit 请求后,会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间占用的事务资源 - 反馈响应
参与者在完成事务提交之后,向协调者发送 ACK 信息 - 完成事务
协调者接收到所有参与者反馈的 ACK 消息后,完成事务
- 发送提交请求
-
中断事务
任何一个参与者反馈了 No 响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事务。- 发送回滚请求
协调者向所有参与者节点发出 Rollback 请求 - 事务回滚
参与者接收到 Rollback 请求后,会利用其在阶段一中记录的 Undo 信息来执行事务回滚操作,并在完成回滚之后释放整个事务执行期间占用的资源 - 反馈响应
参与者在完成事务回滚之后,向协调者发送 ACK 信息 - 中断事务
协调者接收到所有参与者反馈的 ACK 信息后,完成事务中断
- 发送回滚请求
2. 存在的问题
(1)同步阻塞
同步阻塞会极大地限制分布式系统的性能。在二阶段提交的执行过程中,所有参与节点都是事务独占状态,当参与者占有公共资源时,那么第三方节点访问公共资源会被阻塞。
(2)单点故障
一旦协调者发生故障,参与者会一直阻塞下去。
(3)数据不一致
在第二阶段中,假设协调者发出了事务 Commit 的通知,但是由于发生了局部网络异常或协调者在尚未发完 Commit 通知之前自身发生了崩溃,导致最终只有部分参与者接收到了 Commit 通知,其余的参与者没有收到通知,一直处于阻塞状态,于是整个分布式系统出现了数据不一致性现象。
(4)太过保守
如果参与者在与协调者通信期间出现故障,协调者只能靠超时机制来判断是否需要中断事务,这个策略比较保守,需要更为完善的容错机制,任意一个节点的失败都会导致整个事务的失败。
三、3PC(3 Phase Commit)
为了解决二阶段协议中的同步阻塞等问题,三阶段提交协议在协调者和参与者中都引入了超时机制,并且把两阶段提交协议的第一个阶段拆分成了两步:询问,然后再锁资源,最后真正提交。
三阶段中的 Three Phase 分别为 CanCommit、PreCommit、DoCommit 阶段。
1、协议说明
第一阶段:CanCommit
3PC 的 CanCommit 阶段其实和 2PC 的准备阶段很像。协调者向参与者发送 Commit 请求,参与者如果可以提交就返回 Yes 响应,否则返回 No 响应。
第二阶段:PreCommit
协调者根据参与者的反应情况来决定是否可以继续事务的 PreCommit 操作。根据响应情况,有以下两种可能。
-
提交事务
假如协调者从所有的参与者获得的反馈都是 Yes 响应,那么就会进行事务的预执行:
- 发送预提交请求
协调者向参与者发送 PreCommit 请求,并进入 Prepared 阶段; - 事务预提交
参与者接收到 PreCommit 请求后,会执行事务操作; - 响应反馈
如果参与者成功执行了事事务操作,则返回 ACK 响应,同时开始等待最终指令。
- 发送预提交请求
-
中断事务
- 发送中断请求
协调者向所有参与者发送 abort 请求 - 中断事务 无论是收到来自协调者的 abort 请求,还是等待超时,参与者都中断事务
- 发送中断请求
第三阶段:DoCommit
该阶段进行真正的事务提交,也可以分为以下三种情况。
-
执行提交
- 发送提交请求
协调者接收到参与者发送的ACK响应后,那么它将从预提交状态进入到提交状态,并向所有参与者发送doCommit请求。 - 事务提交
参与者接收到doCommit请求之后,执行正式的事务提交,并在完成事务提交之后释放占有的事务资源。 - 响应反馈
事务提交完之后,向协调者发送 ACK 响应。 - 完成事务
协调者接收到所有参与者的 ACK 响应之后,完成事务。
- 发送提交请求
-
中断事务
假设协调者正常工作,并且有任一参与者反馈 No,或者在等待超时后无法接收所有参与者的反馈,都会中断事务。
-
超时提交
参与者如果没有收到协调者的通知,超时之后会执行 Commit 操作。
2. 存在的问题
三阶段提交协议同样存在问题,具体表现为,如果参与者接收到了PreCommit消息后,出现了不能与协调者正常通信的问题,在这种情况下,阶段三中参与者依然会进行事务的提交,这就出现了数据的不一致性。
3. 优点与改进
-
引入超时机制
在 2PC 中,只有协调者拥有超时机制,如果在一定时间内没有收到参与者的消息则默认失败,3PC 同时在协调者和参与者中都引入超时机制。
-
添加预提交阶段(preCommit)
在2PC的准备阶段和提交阶段之间,插入一个准备阶段,使3PC拥有CanCommit、PreCommit、DoCommit三个阶段,PreCommit是一个缓冲,保证了在最后提交阶段之前各参数节点的状态是一致的。
四、2PC 和 3PC 的应用
2PC 是一种比较精简的一致性算法/协议,很多关系型数据库都是采用 2PC 协议来完成分布式事务处理的,典型的比如 MySQL 的 XA 规范。
在事务处理、数据库和计算机网络中,2PC 协议提供了分布式设计中的数据一致性的保障,整个事务的参与者要么一致性全部提交成功,要么全部回滚。MySQL Cluster内部数据的同步就是用的 2PC 协议
1. MySQL 的主从复制
在 MySQL 中,二进制日志是 server 层,主要用来做主从复制和即时点恢复时使用的;而事务日志(Redo Log)是 InnoDB 存储引擎层,用来保证事务安全的。
在数据库运行中,需要保证 Binlog 和 Redo Log 的一致性,如果顺序不一致, 则意味着 Master-Slave 可能不一致。
在开启 Binlog 后,如何保证 Binlog 和 InnoDB redo 日志的一致性呢?MySQL 使用的就是 2 PC,内部会自动将普通事务当做一个 XA 事务(内部分布式事务)来处理:
- Commit 会被自动的分成 Prepare 和 Commit 两个阶段;
- Binlog 会被当做事务协调者(Transaction Coordinator),BinlogEvent 会被当做协调者日志。
五、参考来源
分布式理论系列(二)一致性算法:2PC 到 3PC 到 Paxos 到 Raft 到 Zab
拉钩专栏:分布式技术原理与实战45讲