分布式一致性协议
Amir H. Payberah 《Distributed Systems Consensus》
[email protected]
Amirkabir University of Technology
问题是什么?
为保证分布式系统的高可靠和高可用性,数据在系统中一般存储多个副本。当某个副本所在的节点出现故障时,分布式系统能够自动将服务切换到其他的副本,从而实现自动容错。同一份数据的多个副本中往往有一个副本为主副本,其他为备副本。从一份数据的角度讲,主副本所在的节点为主节点,备副本所在的节点为备节点。但在整个系统范围上看,每个节点即是主节点,也是备节点。
由于同一份数据在整个系统中存在多个副本,因此需要解决如何保证多个副本的一致性问题,以及当主节点出现故障时,如何从多个备节点中选举新主节点的问题。
为解决这些问题,需要做到:
- 使主副本数据状态是确定的(状态机);
- 复制主副本数据到其他备节点
- 确保数据状态的改变以相同的顺序被复制到备节点上,保证得到正确的数据副本。
采用什么协议成了关键点。
一致性协议问题
多个节点间的一致性协议过程,简单来讲就两步:
- 某些节点提出建议值( 或者行动),并发送给其他节点;
- 所有节点必须决定是接受还是拒绝这个建议。
但是,理解是丰满地现实却是骨感......:
- 并发进程与时间、事件和输入的不确定性;
- 机器/进程的故障与恢复,通讯渠道的故障与恢复。
一致性协议要求
1. 安全性
- 有效性:在被提出的建议值中,只有一个可以被选定;
- 一致性: 没有任何两个确定的节点选择不同的值;
- 完整性:一个节点最多只能选择一次。
2. 存活性
- 可终止性: 每个确定的节点,最终都会选择一个值。
分布式系统协议:可能的解决方案
- ?两阶段提交(2PC)协议
- Paxos协议
(最近Raft协议又在坊间流传、使用,据传说比Paxos协议简单,当然有效是必须地。具体情况,我们接下来再聊。)
两阶段提交(2PC:The Two-Phase Commit)
两阶段提交(2PC)问题
- 此问题首先是在数据库系统遇到;
- 假设数据库系统正在更新某些复杂的数据结构,此数据结构由保存在多台机器上的多个部分组成;
- 系统模型:
- 并发进程与时间、事件和输入的不确定性(异步系统);
- 机器/进程的故障与恢复,通讯渠道的故障与恢复。
一个直观的例子
- ?你想跟三个朋友在星期二下午6点组织郊游, 只有所有人都同意才能出行;
- 你怎么做呢?
- 打电话给每个人,问他们星期二下午6点是否可行?(投票阶段voting phase)
- 如果所有人都觉得时间可以,打电话给每个人,告诉他们事已确定;(提交commit)
- 如果有人说没时间去不了,打电话给其他三人,告诉他们出行取消。(中止abort)
- 关键细节:
- 当你打电话给每个人询问时,凡是承诺(promised)周二下午6点能行的人,必须留出时间(must
reserve that slot); - 你需要记下这些决定(remember the decision),然后在提交/中止阶段告诉每个人( tell anyone):谁哪没有达成。
- 当你打电话给每个人询问时,凡是承诺(promised)周二下午6点能行的人,必须留出时间(must
- 这正是两阶段提交是如何工作的。
两阶段提交(2PC)中的角色
- 协调者Coordinator(事务管理器Transaction
Manager)- 开始事务;
- 负责提交/中止(commit/abort)事务。
- 参与者Participants(资源管理器Resource Managers)
- 分布式事务中的数据服务器。
两阶段提交(2PC)算法
阶段1 - 准备prepare阶段
- ?协调者(Coordinator)
询问每个参与者(participant)能否提交(canCommit)? - 参与者(Participants)
必须为提交(commit) 数据修改到持久化存储(storage)做好准备,然后才能回复“yes“。- 锁定(Lock)
涉及到的数据对象。 - 参与者在对协调者(Coordinator) 的能否提交(canCommit)询问回复“ yes”后,不允许(not
allowed)引发事务中止(abort)。
- 锁定(Lock)
- 这时事务的结果还是不确定的(uncertain)
,这种状态一直持续到最终事务提交(doCommit)或者事务中止(doAbort)。- 其他参与者(Other
participants) 仍有可能引发中止(abort)。
- 其他参与者(Other
阶段2 - 提交commit阶段
- ?协调者(coordinator) 汇集所有投票(all votes)。
- 如果全体一致地投票“ yes”(unanimous
yes), 则引发提交。 - 如果任一参与者投票“ no”(any
participant voted no), 则引发中止。
- 如果全体一致地投票“ yes”(unanimous
- 一旦所有参与者(participants)全部投票完毕,事务的命运由协调者( coordinator)自动(atomically)决策。
- 协调者使用持久化存储(permanent
storage)记录最后决策。 - 然后向参与者广播(broadcasts)决策:事务提交 (doCommit)
或者事务中止(doAbort)。
- 协调者使用持久化存储(permanent
?
两阶段提交(2PC)事件时序
?两阶段提交(2PC)的恢复
主要的恢复类型
- 在处理超时后恢复。
- 在系统崩溃与重启后恢复。
- 在一个弹性的异步网络环境中,不能区分上面的这两个情况。
超时处理
- 避免进程永久阻塞情况发生。
- 两种阻塞场景:
- 协调者(Coordinator ) 等待参与者( participants)的投票。
- 参与者(Participant)等待最终决策 ( final decision)。
协调者超时处理
- 如果B投票“no”,协调者能否单方面中止事务?
- 如果B投票“yes“,协调者能否单方面中止/提交事务?
- 协调者等待参与者投票
- 参与者等待最后的决策
- 协调者超时后中止,并且发送事务中止(doAbort)给参与者。
参与者超时处理
- 如果B与事务管理器(协调者)间超时,并且已经回复协调者投票” yes“,那么执行终止协议。
- 简单终止协议(Simple protocol): 参与者保持阻塞(remained blocked) ,直到与协调者重新建立通讯。
- 协作终止协议(Cooperative protocol): 参与者发送一个决策请求(decision-request)消息给其他参与者(other participants)。比如:
- B发送状态消息给A
- 如果A已经从事务管理器(协调者)收到事务提交/事务中止(commit/abort)指令, ...
- 如果A还未投票给事务管理器,A如果还没决定,那它也没法帮到B,A如果单方面决定abort,那它可以回复B一个aboort,B执行相应操作。
- 如果A已经投了yes,但还未收到事务管理器的决定,那A无法帮到B;
如果A已经投了no,那A可以回复B一个abort,B执行相应操作。
崩溃处理与恢复
- 所有节点必须把协议进展记入日志(log protocol progress)。
-  参与者(Participants):prepared->uncertain->committed/aborted
-  协调者(Coordinator):prepared->committed/aborted->done
- 如果已经决策提交事务(commit is decided),所有节点不能再退出( cannot back out )。
- 协调者(Coordinator)崩溃,恢复时:
- 如果从磁盘日志中未找到提交(no commit ),那么进行事务中止过程(aborts)。
- 如果从磁盘日志中找到提交( commit), 那么进行事务提交过程(commits)。
- 参与者(Participant) 崩溃:
- 如果找到事务提交(commits)或者事务中止(aborts)记录,表明在崩溃前已经达成决策,进行相应处理即可。
- 如果从磁盘日志中未找到yes投票(no yes),那么进行事务中止过程(aborts)。
- 如果从磁盘日志中找到yes投票( yes),那么运行终止协议(termination protocol )来达成决策。
2PC容错局限性
- ?即使启用了恢复机制,两阶段提交也达不到真正的容错(fault-tolerant),因为当有一个(或几个)机器故障(fail),可能会被阻塞(blocked )。
- 阻塞意味着故障期间处理不会有进展。(not make progress during the failures)。
- 来个场景秀一下?
2PC阻塞场景
- 事务管理器发达事务提交(doCommit)决策到A,A收到后进行提交处理,然后事务管理器和A都死掉(both TM and A die)。
- B、C、D同样也已经回复投票yes, 并且锁定了(locked)它们本身的一些互斥锁,那么现在需要等待事务管理器或A活过来。
- 没有确定的决策,他们不能进行恢复动作(cannot recover),直到事务管理器或者A重新在线。
- 这就是为何两阶段提交(2PC)被叫做阻塞式协议(blocking protocol):2PC是安全的,但不是具有活性的。
Paxos协议
唯一已知完全安全(completely-safe)与 巨大活性(largely-live)的一致性协议.。
Paxos协议中的角色
- ?提议者(Proposers)
- 提出提案值供投票者考虑。
- 投票者(Acceptors)
- 考虑提议者提出的提案值。
- 回复一个批准/拒绝决策。
- 学习者(Learners )
- 学习已经通过的提案值。
- 一个节点能扮演一个以上的角色。
单个提案(Proposal),单一投票者(Acceptor)
- ?只使用一个投票者(acceptor)。
- 收集多个提议者(Proposers)的提案( proposals )。
- 决定通过的值,告知其他所有人。
- 听起来熟悉吗?
- 两阶段提交(2PC)。
- 投票者(acceptor)故障 = 协议阻塞。
单个提案(Proposal),多个投票者(Acceptor)
- 单一投票者不能充分地容错。
- 让我们设置多个投票者(acceptor)。
- 必须达成一个决策,如何做?
- 决策 = 被大多数所批准的值。
- 原则1:一个投票者(acceptor)必须批准所收到的第一个提案(proposal)。
- 原则1a:当且仅当投票者(Acceptor)没有收到编号大于n的Prepare请求时,投票者(Acceptor)批准编号为 n的提案(Proposal)。
多个提案(Proposal),多个投票者(Acceptor)
- 如果有多个提案(multiple proposals),没有提案(no
proposal)能得到多数票(majority)。 - 如图,3个提案,每个可能获得1/3的投票者。
- 解决方案:投票者能批准多个提案(accept multiple proposals),通过一个唯一的提案编号(unique
proposal number)区分。
多个提案(Proposal)的处理
- 所有被通过的提案(chosen proposals)必须是相同的提案值(the
same value)。 - 原则2:如果一个提案的值v被通过,那么之后被通过的每个更高编号的提案,必须具有相同的提案值。
- P2a:一旦一个值v被通过,那么之后任何投票者(Acceptor)再批准的值必须是v。
- P2b:一旦一个值v被通过,那么以后提议者(Proposer)提出的新提案必须具有值v。
- P2c:如果一个编号为 n 的提案具有值v,那么存在一个多数派,要么他们中没有人批准过编号小于 n的任何提案,要么他们进行的最近一次通过具有值v。
Paxos算法
- 阶段1a - 准备(Prepare)
提议者(Proposer) 选择一个提案编号 N,并将包含了该编号的Prepare(N)请求,发送给所有投票者(Acceptor) 中的一个多数派(majority);
- 阶段1b - 承诺(Promise)
投票者(Acceptor) 收到 Prepare (N)消息后,当N大于所有它以前承诺过(promise)或收到Preapare请求的编号,
- 承诺不再批准编号小于N的提案,
- 发送响应promise(N,U),U是上次批准的最大编号提案(如果有的话)。
- 阶段2a - 请求批准(Accept!)
如果提议者(Proposer) 收到了多数派的响应,向这些回复 Prepare(N) 请求的投票者(Acceptor),发送Accept(N,V)请求,V是收到的promise响应中最大编号提案的值,如果没有promise包含提案,那么可自由定值。
- 阶段2b - 批准(Accepted)
如果N大于等于任何已经承诺过(proimise)的提案编号,
- 批准这个提案
- 发送一个accepted通知给提议者或学习者。
Paxos示例
Paxos安全性
- 如果编号为n的提案包含的值v已经被通过,在第二轮(2a、2b)发出的后续更大编号提案,必须包含相同的值v。
- 决策 = 多数派 (任意两个派至少共享一个成员)。
- 因此,第一轮(1a、1b)有了决策后, 随后的一轮(2a、2b)中至少有一个投票者已经批准过值v。
- 现在假设我们的说法是不正确的,在后面第二轮(2a、2b)中有一个编号为m首次提案(m晚于n),提议中的值是w且不等于v。
- 这是不可能的,因为如果这个提议者P可以用值w启动第二轮(2a、2b),说明它已经让多数派批准过一轮提案m(m>n)。所以,会有矛盾的结论:
- v 未被决策通过,与
- v 被P提案过
- 因此,一旦有多数派批准过值v ,就不会被改变。
Paxos活性
............
Paxos的实现
- Google:Chubby
- Yahoo:Zookeeper
- .........
原文来自于:www.slideshare.net 《Distributed Systems Consensus》 PPT
译 者:歪脖大肚子Q