目录
2PC 协议
3PC 协议
Paxos 协议
简介
分布式系统涉及的协议很多,例如租约,复制协议,一致性协议,其中以两阶段提交协议(2PC)和Paxos协议最具有代表性。两阶段提交协议用于保证跨多个节点操作的 原子性,也就是说,跨多个节点的操作要么在所有节点上全部执行成功,要么全部失败。Paxos协议用于确保多个节点对某个投票(例如哪个节点为主节点)达成一致。
2PC 协议
两阶段提交又称 2PC,是一个非常经典的 强一致、中心化的原子提交协议。两阶段提交协议经常用来实现分布式事务,在两阶段协议中,系统一般包含两类节点:一类是 协调者,通常一个系统中只有一个;另一类是事务的 参与者,一般包含多个。协议中假设每个节点都会记录操作日志并持久化到非易失性存储介质,即使节点发生故障日志也不会丢失。顾明思义,两阶段提交协议由两个阶段组成。在正常的执行过程中,这两个阶段的执行过程如下所述:
阶段1:请求(投票)阶段。在请求阶段,协调者通知事务的参与者准备提交或者取消事务,然后进入表决过程。在表决过程中,参与者将告知协调者自己的决策:同意(事务参与者本地执行成功)或者取消(事务参与者本地执行失败)。
阶段2:提交阶段。在提交阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消。当且仅当所有的参与者都同意提交事务,协调者才能通知所有的参与者提交事务,否则协调者通知所有的参与者取消事务。参与者在接收到协调者发来的消息后将执行相应的操作。
举例:订单服务 A,需要调用 支付服务 B 去支付,支付成功则处理购物订单为待发货状态,否则就需要将购物订单处理为失败状态。看 2PC 是如何处理的。
第一阶段主要细分为 3 步
① 事务询问
协调者 向所有的 参与者 发送事务预处理请求,称之为 Prepare,并开始等待各 参与者 的响应。
② 执行本地事务
各个 参与者 节点执行本地事务操作,但在执行完成后并不会真正提交数据库本地事务,而是先向 协调者 报告说:"我这边可以处理了/我这边不能处理"。
③ 各参与者向协调者反馈事务询问的响应
如果 参与者 成功执行了事务操作,那么就反馈给协调者 Yes 响应,表示事务可以执行。如果没有 参与者 成功执行事务,那么就反馈给协调者 No 响应,表示事务不可以执行。
第一阶段执行完后,会有两种可能。(1) 所有都返回 Yes。(2) 有一个或者多个返回 No。
第二阶段根据不同的情况做出相应的操作
成功流程:
① 所有的参与者反馈给协调者的信息都是 Yes,那么就会执行事务提交。
协调者 向所有 参与者 节点发出 Commit 请求。
② 事务提交。
参与者 收到 Commit 请求之后,就会正式执行本地事务 Commit 操作,并在完成提交之后释放整个事务执行期间占用的事务资源。
失败流程:
条件:任何一个参与者 向 协调者 反馈了 No 响应,或者等待超时之后,协调者尚未收到所有参与者的反馈响应。
① 发送回滚请求
协调者 向所有 参与者 节点发出 RoollBack 请求。
② 事务回滚
参与者 接收到 RoollBack 请求后,会回滚本地事务。
2PC 缺点
通过上面的演示,很容易想到 2PC 所带来的缺陷。
① 性能问题
无论是在第一阶段的过程中,还是在第二阶段,所有的参与者资源和协调者资源都是被锁住的,只有当所有节点准备完毕,事务 协调者 才会通知进行全局提交,参与者 进行本地事务提交后才会释放资源。这样的过程会比较漫长,对性能影响比较大。
② 单节点故障
由于 协调者 的重要性,一旦 协调者 发生故障。参与者 会一直阻塞下去。尤其在第二阶段,协调者 发生故障,那么所有的 参与者 还都处于锁定事务资源的状态中,而无法继续完成事务操作。
2PC 可能面临的故障
(1) 协调者正常,参与者宕机
由于 协调者 无法收集到所有 参与者 的反馈,会陷入阻塞情况。
解决方案:引入超时机制,如果协调者在超过指定的时间还没有收到参与者的反馈,事务就失败,向所有节点发送终止事务请求。
(2) 协调者宕机,参与者正常
无论处于哪个阶段,由于 协调者 宕机,无法发送提交请求,所有处于执行了操作但是未提交状态的参与者都会陷入阻塞情况。
解决方案:引入协调者备份,同时协调者需记录操作日志。当检测到协调者宕机一段时间后,协调者备份取代协调者,并读取操作日志,向所有参与者询问状态。
(3) 协调者和参与者都宕机
① 发生在第一阶段:因为第一阶段,所有参与者都没有真正执行 Commit,所以只需重新在剩余的参与者中重新选出一个协调者,新的 协调者 在重新执行第一阶段和第二阶段就可以了。
② 发生在第二阶段 并且 挂了的参与者在挂掉之前没有收到协调者的指令。也就是上面图片中的第 4 步挂了,这是可能协调者还没有发送第 4 步就挂了。这种情形下,新的协调者重新执行第一阶段和第二阶段操作。
③ 发生在第二阶段 并且 有部分参与者已经执行完 commit 操作。就好比这里 订单服务A 和 支付服务B 都收到 协调者 发送的 Commit 信息,开始真正执行本地事务 Commit,但突发情况,A Commit 成功,B 却挂了。这个时候目前来讲数据是不一致的。虽然这个时候可以再通过手段让他和协调者通信,再想办法把数据搞成一致的,但是,这段时间内他的数据状态已经是不一致的了! 2PC 无法解决这个问题。
3PC 协议
三阶段提交(Three-phase commit),是二阶段提交(2PC)的改进版本。三阶段提交协议(3PC)主要是为了解决两阶段提交协议的阻塞问题,2PC 存在的问题是当 协作者 崩溃时,参与者 不能做出最后的选择。因此参与者可能在协作者恢复之前保持阻塞。改进措施:
(1) 引入超时机制,同时在 协调者 和 参与者 中都引入超时机制。
(2) 在 2PC 的第一阶段和第二阶段中插入一个准备阶段,保证了在最后提交阶段之前各参与节点的状态是一致的。
也就是说,除了引入超时机制之外,3PC 把 2PC 的准备阶段再次一分为二,这样三阶段提交就有 CanCommit、PreCommit、DoCommit 三个阶段。
CanCommit 阶段
之前 2PC 的第一阶段是本地事务执行结束后,最后不 Commit,等其它服务都执行结束并返回 Yes,由协调者发生 Commit 才真正执行 Commit。而这里的 CanCommit 指的是尝试获取数据库锁,如果可以,就返回 Yes。
该阶段主要分为 2 步:
① 事务询问:协调者 向 参与者 发送 CanCommit 请求。询问是否可以执行事务提交操作。然后开始等待 参与者 的响应。
② 响应反馈:参与者 接到 CanCommit 请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回 Yes 响应,并进入预备状态。反之,反馈 No。
PreCommit 阶段
在收到所有的参与者都返回的 Yes 后,就会进入 PreCommit 阶段进行事务预提交。这里的 PreCommit 阶段 跟 2PC 的第一阶段是差不多的,只不过这里 协调者和参与者都引入了超时机制 (2PC 中只有协调者可以超时,参与者没有超时机制)。
DoCommit 阶段
这里跟 2PC 的第二个阶段是差不多的。
相比较 2PC 而言,3PC 对于 协调者(Coordinator)和 参与者(Partcipant)都设置了超时时间,而 2PC 只有协调者才拥有超时机制。这避免了参与者在长时间无法与协调者节点通讯(协调者挂掉了)的情况下,无法释放资源的问题,因为参与者自身拥有超时机制会在超时后,自动进行本地 Commit 从而进行释放资源。而这种机制也侧面降低了整个事务的阻塞时间和范围。
另外,通过 CanCommit、PreCommit、DoCommit 三个阶段的设计,相较于 2PC 而言,多设置了一个缓冲阶段保证了在最后提交阶段之前各参与节点的状态是一致的。以上就是 3PC 相对于 2PC 的一个提高(相对缓解了 2PC 中的前两个问题),但是 3PC 依然没有完全解决数据不一致的问题。
Paxos 协议
Paxos 协议用于解决多个节点之间的数据一致性问题。多个节点之间通过 操作日志 同步数据,如果只有一个节点为主节点,那么,很容易确保多个节点之间操作日志的一致性。考虑到主节点可能出现故障,系统需要选举出新的主节点。Paxos 协议正是用来实现这个需求。只要保证了多个节点之间操作日志的一致性,就能够在这些节点上构建高可用的全局服务,例如分布式锁服务,全局命名和配置服务等。
为了实现高可用性,主节点往往将数据以 操作日志 的形式同步到备节点。如果主节点发生故障,备节点会提议自己成为主节点。这里存在的问题是网络分区的时候,可能会存在多个备节点提议(Proposer,提议者)自己成为主节点。Paxos 协议保证,即使同时存在多个提议者,也能够保证所有节点最终达成一致,即选举出唯一的主节点。
大多数情况下,系统只有一个提议者,它的提议也总是会很快地被大多数节点接受,Paxos 协议执行步骤如下:
(1) 批准(accept):提议者 发送 accept 消息要求所有其他节点接受某个提议值,接受者 可以接受或拒绝。
(2) 确认(acknowledge):如果超过一半的 接受者 接受,意味着提议值已经生效,提议者 发送确认消息通知所有的 接受者 提议生效。
当出现网络或者其他异常时,系统中可能存在多个提议者,它们各自发起不同的提议。这里的提议可以是一个修改操作,也可以是提议自己成为主节点。如果提议者第一次发起的接受请求没有被接受者中的多数派批准,那么,需要完整地执行一轮 Paxos 协议。过程如下:
(1) 准备:提议者 首先选择一个提议序号 n 给其他的接收节点发送准备消息。接受者 收到准备消息后,如果提议的序号大于他已经回复的所有准备消息,则 接受者 将自己上次接受的提议回复给 提议者,并承诺不再回复小于 n 的提议。
(2) 批准:提议者 收到了 接受者 中的多数派对准备的回复后,就进入批准阶段。如果在之前的准备阶段 接受者 回复了上次接受的提议,那么,提议者 选择其中序号最大的提议值发给 接受者 批准;否则,提议者 生成一个新的提议值发给 接受者 批准。接受者 在不违背他之前在准备阶段的承诺的前提下,接受这个请求。
(3) 确认:如果超过一半的 接受者 接受,提议值生效。提议者 发送确认消息通知所有的 接受者 提议生效。
Paxos 协议需要考虑两个问题:正确性,即只有一个提议值会生效;可终止性,即最后总会有一个提议值生效。Paxos 协议中要求每个生效的提议被接受者中的多数派接受,并且每个接受者不会接受两个不同的提议,因此可以保证正确性。Paxos 协议并不能够严格保证可终止性。但是,从 Paxos 协议的执行过程可以看出,只要超过一个接受者接受了提议,提议者很快就会发现,并重新提议其中序号最大的提议值。因此,随着协议不断运行,它会往 "某个提议值被多数派接受并生效" 这一最终目标靠拢。
Paxos 协议和 2PC 协议在分布式系统中所起的作用并不相同。Paxos 协议用于保证同一个数据的多个副本之间的数据一致性。当这些副本分布到不同的数据中心时,这个需求尤其强烈。2PC 协议用于保证多个数据上的操作的原子性。这些数据可能分布在不同的服务器上,2PC 协议保证多台服务器上的操作要么全部成功,要么全部失败。
Paxos 协议有两种用法:一种用法是用它来实现全局的锁服务或者命名和配置服务,例如Google Chubby以及Apache Zookeeper。另外一种用法是将用户数据复制到多个数据中心,例如Google Megastore和Goolge Spanner。
2PC 协议最大的缺陷是无法处理 协调者宕机 问题。如果协调者宕机,那么,2PC 协议中的每个参与者可能都不知道事务应该提交还是回滚,整个协议被阻塞,执行过程中申请的资源都无法释放。因此,常见的做法是将 2PC 和 Paxos 协议结合起来,通过 2PC 保证多个数据上的操作的 原子性,通过 Paxos 协议实现同一个数据的多个副本之间的 一致性。另外,通过 Paxos 协议解决 2PC 协议中协调者宕机的问题。当 2PC 协议中的协调者出现故障时,通过 Paxos 协议选举出新的协调者继续提供服务。
参考:百度百科、https://www.cnblogs.com/qdhxhz/p/11167025.html