随着计算器系统规模变得越来越大,计算机系统正在经历一场前所未有的从集中式到分布式架构的变革,相信有过分布式开发经验的都能明白其痛点--分布式一致性。Zookeeper的出现帮助很多系统在一定程度上解决了这个难点,使用也非常简单,作为分布式一致性问题的工业解决方案,paxos是理论算法,其中zab,raft和众多开源算法是对paxos的工业级实现。这本书是本人很早就想看的书了,在刚开始使用zookeeper时就听过这一句话:Zookeeper的强大在于Paxos,而Paxos算法的难理解与算法的知名度一样令人敬仰,接下来这段时间我们一起从浅到深的学习一下Paxos以及在Zookeeper中的应用。
首先还是介绍一些基础的理论知识,从理论的角度来理解所谓分布式的最大痛点。
ACID即事务的特性,每次面试都会问的,这里就只做简单介绍了,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),其中,事务的隔离级别:读未提交(允许脏读)、读已提交(允许不可重复读)、可重复读(允许幻读)和串行化,从隔离级别回头看看为什么需要事务,这里不仅仅是指狭义的数据库事务,包括系统中任何一系列的对数据进行访问以及更新的操作。一方面,在并发访问时,事务可以在各个应用程序之间提供一个隔离方法,防止相互干扰;第二,事务为数据库操作序列提供一个从失败恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持数据一致性的方法。
随着分布式计算的发展,事务在分布式计算领域中也得到了广泛的应用。在单机数据库中,我们很容易能够实现一套满足ACID特性的事务处理系统,现在各大框架对事务的支持也十分健壮,但是在分布式数据库中,数据分散在不同的机器上,如何对这些数据进行分布式的事务处理具有非常大的挑战,这也是前面所说的分布式的最大痛点---分布式数据一致性!
为什么会出现不一致的问题呢?这是分布式系统的特点,带来优势也同时带来劣势,为了性能以及可用性,分布式系统都会有多个节点存储数据,不同的数据节点之间由于网络延时等原因很容易产生数据不一致的情况。复制机制的目的是为了保证数据的一致性。但是数据复制面临的主要难题也是如何保证多个副本之间的数据一致性。
对分布式数据一致性简单的解释就是:当对集群中一个副本数据进行更新的同时,必须确保能够同步更新到其他副本,否则不同副本之间的数据将不再一致。举个例子来说就是:当客户端C1将系统中的一个值K由V1更新为V2,但是客户端C2读的是另一个还没有同步更新的副本,K的值依然是V1,这就导致了数据的不一致性。其中,常见的就是主从数据库之间的复制延时问题,这是传统的ACID模型无法保障的,于是便有了CAP和BASE。
CAP即一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三个基本需求,但是这个理论在提出的同时,也证明了这三项最多同时满足两项。
放弃P:如果想避免分区容错性问题的发生,一种做法是将所有的数据(与事务相关的)都放到一台机器上。虽然无法100%地保证系统不会出错,但不会碰到由分区带来的负面效果。当然,这个选择会严重影响系统的扩展性。
放弃A:相对于放弃“分区容错性”来说,其反面就是放弃可用性。一旦遇到分区容错故障,那么受到影响的服务需要等待数据一致,因此在等待期间系统就无法对外提供服务。
放弃C:这里所说的放弃一致性,并不是完全放弃数据的一致性,而是放弃数据的强一致性,而保留数据的最终一致性。以网络购物为例,对只剩最后一件库存的商品,如果同时接收到了两份订单,那么较晚的订单将被告知商品售罄。
CAP无法同时满足,现有分布式系统是如何取舍呢?明确的一点是,P是一个分布式系统最基本的要求,不然也不能叫分布式系统了,可扩展、高可用以及快速响应是分布式系统的初衷,因此架构师往往需要把精力花在如何根据业务特点在C和A之间需求平衡。
BASE:基本可用(Basically Available)、软状态(Soft state)和最终一致性(Eventually consistent),某一次面试中面试官问了我一个让我印象特别深刻的问题:你觉得BASE是一种妥协吗?我当时心底冒出的是士兵突击里说许三多的一句话:“至少面对无法解决的态势,他想了办法,尽了力,第一次有人这么做”。BASE是对CAP中一致性和可用性权衡的结果,其核心思想是即使无法做到强一致性,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。
总的来说,BASE面向的是大型高可用可扩展的分布式系统,和传统的ACID特性是相反的,通过牺牲强一致性来获得可用性,并允许数据的软状态以及最终一致性,但是在实际的分布式场景中,不同的业务对数据一致性的要求不同,ACID和BASE往往会结合使用。
这一章非常重要,也是在长期的探索研究过程中,从二阶段提交协议到三阶段提交协议再到Paxos,首先我们介绍2PC和3PC。
在分布式系统中,每一个机器节点虽然都能明确知道自己在进行事务操作过程中是成功还是失败,但是对其他节点却不够了解。因此,当一个事务操作需要跨越多个分布式节点时,为了保持ACID特性,就需要引入“协调者”的组件来统一调度所有分布式节点的执行逻辑,被调度的节点叫做“参与者”。协调者负责调度参与者的行为,并最终决定这些参与者是否要把事务真正的提交,基于这个思想,衍生出了2PC和3PC。
2PC即Two-Phase Commit的缩写,即二阶段提交,二阶段提交协议说明如下:
阶段一:提交事务请求(投票阶段)
1、事务询问:协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者的响应;
2、执行事务:各参与者节点执行事务操作,并将Undo和Redo信息记入事务日志中;
3、各参与者向协调者反馈事务询问的响应:参与者反馈Yes表示事务可以执行,返回No则表示事务不可以执行;
阶段二:执行事务提交(执行阶段)
该阶段是根据阶段一的结果来决定是否提交事务,因此包含两种可能性:
执行事务提交:
1、发送提交请求:协调者向所有参与者发出Commit请求;
2、事务提交:参与者接收到Commit请求后,会正式执行事务提交操作,并在完成提交之前释放整个事务执行期间占用的事务资源;
3、反馈事务提交结果:参与者在完成事务提交后,向协调者发送ack消息;
4、协调者接收到所有参与者反馈的ack后,完成事务;
中断事务:
1、发送回滚请求:协调者向所有参与者节点发出RollBack请求;
2、事务回滚:参与者接收到RollBack请求后,会利用其在一阶段中记录的Undo信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源;
3、反馈事务回滚结果:完成回滚后,向协调者发送ack;
4、中断事务:当协调者接到所有参与者反馈的ack时,完成事务中断;
2PC简单的来说就是将事务的提交划分为投票-提交,其核心是对每个事务都采用先尝试后提交的处理方式,用图描述“事务提交”和“中断事务”如下:
2PC的思路还是比较简单的,也被广泛应用在关系型数据库的分布式事务处理中,利用该协议能够非常方便的完成所有分布式事务参与者的协调,统一决定事务的提交或者回滚,从而能够有效地保证分布式数据一致性。
优点:原理简单,实现方便;缺点:同步阻塞、单点问题、脑裂以及太过保守,着重介绍下其缺点:
3PC即三阶段提交,是2PC的改进版,将事务提交过程分成CanCommit、PreCommit、DoCommit阶段,用图描述如下:
阶段一:CanCommit
1、事务询问:协调者向所有参与者询问是否可以执行事务提交操作,并开始等待各参与者的响应;
2、各参与者向协调者反馈事务询问的响应:参与者如果认为自己可以顺利执行事务,则反馈YES,否则反馈No;
阶段二:PreCommit(该阶段类似2PC的执行阶段,通过收集阶段一的投票,决定事务是否能够正常提交,因此也会包括两种情况)
执行事务预提交
1、发送预提交请求:协调者向所有参与者发送preCommit请求,进入Prepared阶段;
2、事务预提交:接收到preCommit请求后,参与者会执行事务操作,并将undo和redo信息记录到事务日志中;
3、各参与者向协调者反馈事务执行的响应:如果参与者成功执行了事务,即反馈Ack,同时等待最终的指令:提交或终止;
中断事务
1、发送中断请求:协调者向所有参与者节点发送终止请求;
2、中断事务:无论是收到协调者的终止请求还是在等待过程中超时,参与者都会中断事务;
阶段三:DoCommit(该阶段执行真正的事务提交,也会存在两种可能的情况)
执行提交
1、发送提交请求:当协调者接收到所有参与者的响应ACK,则从预提交状态切换到提交状态,并向所有的参与者发送doCommit请求;
2、事务提交:参与者收到doCOmmit请求后,会正式执行事务提交操作,并在提交完成后释放占用资源;
3、反馈事务提交结果:完成事务提交后向协调者响应Ack;
4、完成事务:协调者在收到参与者响应的Ack后,完成事务;
中断事务
1、发送中断请求
2、事务回滚:当参与者接收到终止请求后,会利用阶段二中的undo日志来执行事务回滚操作,完成回滚后同样要释放占用资源;
3、反馈事务回滚结果:参与者将回滚结果的Ack反馈给协调者;
4、中断事务 ;
3PC的优缺点
优点:相比2PC,降低了参与者阻塞的范围(三个阶段划分粒度变小),并且能在出现单点故障后达成一致;缺点:参与者接收到preCommit消息后,如果网络出现了分区,此时协调者所在的节点和参与者无法进行正常的网络通信,在事务执行的过程中无法扩展,个人理解是引入了另一种阻塞,如果不阻塞,那必然就会出现数据不一致的情况;
了解2PC和3PC是我们学习Paxos的前提,但学完了之后发现,它们并没有彻底解决分布式的痛点,一致性问题,Google Chubby的作者Mike Burrows说过,世上只有一种一致性算法,那就是Paxos,所有其他一致性算法都是Paxos算法的不完整版,下一篇将全篇详细介绍Paxos算法,真的很难很难!