《企业级大数据平台构建:架构与实现》——2.2.6 原子消息广播协议
2.2.6 原子消息广播协议
ZAB(Zookeeper Atomic Broadcast)是一种数据分布式一致性算法,是Paxos算法的简化版本。可以说ZAB是Zookeeper立足的根本。
在讲解ZAB协议之前,我们先简单回顾一下分布式一致性的发展历程。
1.?两阶段提交协议
在单机数据库时代,我们没有分布式数据一致性问题的困扰。通过数据库事务我们就能轻松达成ACID的特性从而保证数据的一致性。
知识扩展
ACID是Atomic(原子性)、Consistency(一致性)、Isolation(隔离性)和Durability
(持久性)的英文缩写。
1)原子性:将一组操作视为一个原子操作,操作要么全部执行,要么全部不执行。
2)一致性:一个事务在执行之前和执行之后数据库都必须处在一致性的状态。
3)隔离性:不同事务之间是相互隔离的。
4)持久性:一单一个事务被提交,它对数据库中数据的改变应该是永久性的。
但是当我们进入分布式服务时代的时候,单机事务就显得力不从心了。在分布式服务的场景下,我们会有多个物理上独立的数据库分布在不同的服务器。从单独的视角来看,每个独立的数据库内部都能通过单机事务保证数据的一致性,但是如果一个操作同时涉及多个独立数据库的时候,就会出现数据不一致的情况。
于是人们设计了一种名叫两阶段提交(Two-PhaseCommit,2PC)的协议来解决这个问题。简单来说两阶段提交就是在客户端和多个数据库之间增加了一个事务协调者,同时将事务的提交分为准备和提交两个阶段。
(1)准备阶段
事务协调者给每个数据库发送prepare消息,每个数据库要在本地执行事务,写本地的redo和undo日志,但不提交事务。
大致流程如下:
1)事务协调者节点向所有数据库询问是否可以执行提交操作,并开始等待各参与者节点的响应。
2)数据库节点执行询问发起为止的所有事务操作,并将undo信息和redo信息写入日志。
3)数据库节点响应事务协调者节点发起的询问。如果数据库节点的事务操作实际执行成功,则返回一个“同意”消息;如果数据库节点的事务操作执行失败,则返回一个“中止”消息。
(2)提交阶段
如果事务协调者收到了数据库的中止消息或者等待超时,则直接给每个数据库发送rollback消息,要求进行回滚操作;如果事务协调者收到了数据库的同意消息,则发送commit消息,要求进行提交操作。执行完毕之后释放所有事务处理过程中使用的资源。
大致流程程如下:
1)事务协调者节点向所有数据库发起commit/rollback请求,并开始等待各参与者节点的响应。
2)数据库节点执行commit/rollback动作并释放事务占用的资源,之后向事务协调者发送完成消息。
3)事务协调者收到所有数据库的反馈消息后完成或取消事务。
两阶段提交看似解决了分布式数据一致性的问题,其实这个设计存在一些明显的问题:
1)阻塞执行、速度慢:从刚才描述可以看出,协调者和数据库的一系列提交或回滚动作都是阻塞执行的,这必然导致整个分布式事务运行效率缓慢。
2)单点问题:两阶段提交的核心枢纽是事务协调者节点,如果这个节点失效了,那么整个事务机制也就瘫痪了。同时由于协调者失效,会导致数据库的资源一直占有无法释放。
3)数据不一致:试想一下,在提交阶段,当事务协调者向所有数据库发送commit
请求之后,由于网络问题只有一部分数据库收到了请求消息并执行了commit动作,而另一部分数据库没有收到commit请求消息,多个数据库之间就会产生数据不一致的问题。
2.?三阶段提交协议
为了解决两阶段提交协议的缺陷,人们又提出了一个改进版本,这就是三阶段提交协议(Three-PhaseCommit,3PC)。将协调者和数据库都引入超时机制,以解决在协调者失效的时候,数据库会一直占有资源无法释放的问题,同时又将准备阶段拆分成了询问和准备两个阶段以增加容错概率。
(1)询问阶段
事务协调者给每个数据库发送CanCommit请求,每个数据库如果可以提交就返回YES消息,否则就返回NO消息。
大致流程程如下:
1)事务协调者节点向所有数据库发起CanCommit请求,并开始等待各参与者节点的响应。
2)数据库节点收到CanCommit请求之后如果可以提交,则返回YES消息并进入准备阶段。否则返回NO消息。
(2)准备阶段
事务协调者收到反馈后会有两种情况产生:
1)数据库返回的消息均为YES消息,则执行事务预执行。大致流程程如下:
①事务协调者节点向所有数据库发起PreCommit请求,并开始等待各参与者节点的响应。
②数据库节点执行到询问发起为止的所有事务操作,并将undo信息和redo信息写入日志。执行成功后返回ACK应答,并进入等待。
2)数据库返回的消息含有NO消息或者等待超时,则执行事务中断。大致流程程如下:
①事务协调者节点向所有数据库发起abort中断请求。
②数据库节点收到abort请求之后执行事务中断。如果在超时之后还没收到事务协调者的任何消息,也执行事务中断动作。
(3)提交阶段
事务协调者收到反馈后会有两种情况产生:
1)协调者收到所有ACK应答,则执行事务提交。大致流程程如下:
①事务协调者节点向所有数据库发起DoCommit请求,并开始等待各参与者节点的响应。
②数据库节点执行执行commit动作并释放事务占用的资源,之后向事务协调者发送ACK消息应答。
③事务协调者收到所有数据库的反馈消息后完成事务。
2)协调者没有收到所有ACK应答,则执行事务中断。大致流程如下:
①事务协调者节点向所有数据库发起abort中断请求。
②数据库节点收到abort请求之后执行事务回滚,并向事务协调者发送ACK消息应答。
③事务协调者收到所有数据库的反馈消息后完成事务中断。
从三阶段提交的流程来看,已经解决了两阶段提交中一些问题,但还是会出现数据不一致的问题。因为当进入第三阶段也就是提交阶段的时候,如果数据库在超时前没有收到DoCommit或abort消息,那么它最终会执行commit动作。
试想一下如果在提交阶段事务协调者没有收到所有ACK应答,那么它会发送abort中断事务的请求。碰巧这个时候网络发生了抖动导致一部分数据库没有收到abort消息,那么收到消息的数据库会执行事务中断,而没有收到abort消息的数据库最终执行为commit动作,这就导致数据不一致了。
3.?Paxos协议
为了完美解决分布式场景下数据一致性的问题,Paxos算法诞生了。Paxos算法原文十分难理解,由于篇幅有限,这里主要是通过作者Leslie Lamport另一篇相对简单的论文《Paxos Made Simple》来进行简单描述。具体的内容可以查看Paxos Made Simple论文的全文。
假设有一组可以提出提案的进程集合,提案用[编号,值]的形式来描述,一致性算法需要保证以下几点:
只有当一个提案被提出后才能被选定。
这些进程集合中只有一个提案会被选定。
如果某个进程认为某个提案被选定了,那么这个提案确实是已经被选定了。
该一致性算法分为三个角色,我们用proposer、acceptor和learner来表示。proposer
可以批准提案,acceptor可以提交提案,而learner只能获取已经被批准的提案。
阶段一:
1)proposer选择一个提案编号n,然后向超过半数的acceptor发送编号n的prepare
请求。
2)如果一个acceptor收到了一个编号为n的prepare请求,并且编号n大于这个acceptor之前已经响应的prepare请求的编号,那么它就会将已经批准过的最大编号的提案作为响应发送给proposer,并且承诺不会再批准任何编号小于n的提案。
阶段二:
1)如果proposer收到了半数以上的acceptor对编号n的prepare请求的回应,那么它就会发送一个针对[n,v]提案的accpet请求给那些acceptor。这里v的值就是收到的响应中编号最大的提案的值。
2)如果acceptor收到了这个针对[n,v]提案的accpet请求,只要该acceptor还没有对编号大于n的提案进行响应,它就会通过这个提案。
Paxos通过引入过半提交的概念解决了在两阶段和三阶段提交协议中会出现的种种问题。正如Chubby的作者Mike Burrows所说的那样,这个世界上只有一种分布式一致性算法,那就是Paxos。
4.?ZAB协议
Paxos算法理论虽然完美,但是实现起来太过复杂,因为它的目标是构建一个去中心化的、通用的分布式一致性算法。而且Paxos算法只在乎数据的一致性而并不关心事务请求的顺序,这一点并不能满足Zookeeper的要求。因为Zookeeper的命名空间是一个树形结构,对执行的顺序有严格要求。于是Zookeeper借助了Paxos过半提交的思想将两阶段提交进行优化改造,ZAB就这样诞生了。
ZAB协议中有三种角色:
Leader:所有的写请求首先都会转发到Leader节点上,Leader节点上的数据变更会同步到集群的Follower节点上。
Follower:负责同步Leader节点的数据,并提供数据的查询功能。当Leader节点失效的时候有权利参与投票选举。
Observer:同步Leader节点的数据,并提供数据的查询功能。没有投票选举。Observer的设计目的是提高集群的查询性能。
和Paxos有所不同,ZAB并不是一个无中心化的架构,在任意时刻ZAB都保持有且仅有一个Leader节点,所有的更新事务都只能由这个Leader发起。并且当一个Leader失效以后,新的Leader只有在之前Leader的事务都被处理之后才能发起新的事务。通过这种机制,ZAB协议保证了全局的事务顺序。在更新阶段ZAB使用的就是一个优化过的两阶段提交,这里借助了Paxos的思想,只要过半的节点prepare成功,就会发起commit请求。如果想查看ZAB完整的协议内容可以阅读它的论文《Zab: High-performance broadcast for primary-backup systems》。
- 点赞
- 收藏
- 关注作者
评论(0)