从分布式系统的CAP理论出发,关注分布式一致性,以及区块链的共识问题及解决。

区块链首先是一个大规模分布式系统,共识问题本质就是分布式系统的一致性问题,但是又有很大的不同。
工程开发中,认为系统中存在故障(fault),但不存在恶意(corrupt)节点,而区块链,特别是公开链是落地到物理世界中,涉及到人性和利益关系,不可避免的存在信任以及恶意攻击问题。

分布式一致性处理的是节点失效情况(即可能消息丢失或重复,但无错误消息)的共识达成(Consensus)问题,主要是Paxos算法及衍生的Raft算法。

一、分布式系统的挑战

关于分布式系统有一个经典的CAP理论,

 
 

CAP理论的核心思想是任何基于网络的数据共享系统最多只能满足数据一致性(Consistency)、可用性(Availability)和网络分区容忍(Partition Tolerance)三个特性中的两个。

  • Consistency 一致性
    一致性指“all nodes see the same data at the same time”,即更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致。等同于所有节点拥有数据的最新版本。

  • Availability 可用性

可用性指“Reads and writes always succeed”,即服务一直可用,而且是正常响应时间。
对于一个可用性的分布式系统,每一个非故障的节点必须对每一个请求作出响应。也就是,该系统使用的任何算法必须最终终止。当同时要求分区容忍性时,这是一个很强的定义:即使是严重的网络错误,每个请求必须终止。

  • Partition Tolerance 分区容忍性

Tolerance也可以翻译为容错,分区容忍性具体指“the system continues to operate despite arbitrary message loss or failure of part of the system”,即系统容忍网络出现分区,分区之间网络不可达的情况,分区容忍性和扩展性紧密相关,Partition Tolerance特指在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务。

提高分区容忍性的办法就是一个数据项复制到多个节点上,那么出现分区之后,这一数据项就可能分布到各个区里。分区容忍就提高了。然而,要把数据复制到多个节点,就会带来一致性的问题,就是多个节点上面的数据可能是不一致的。要保证一致,每次写操作就都要等待全部节点写成功,而这等待又会带来可用性的问题。

 

如图,Client A可以发送指令到Server并且设置更新X的值,Client 1从Server读取该值,在单点情况下,即没有网络分区的情况下,或者通过简单的事务机制,可以保证Client 1读到的始终是最新的值,不存在一致性的问题。

如果在系统中增加一组节点,Write操作可能在Server 1上成功,在Server 1上失败,这时候对于Client 1和Client 2,就会读取到不一致的值,出现不一致。如果要保持x值的一致性,Write操作必须同时失败,降低系统的可用性。

可以看到,在分布式系统中,同时满足CAP定律中“一致性”、“可用性”和“分区容错性”三者是不可能的。

在通常的分布式系统中,为了保证数据的高可用,通常会将数据保留多个副本(replica),网络分区是既成的现实,于是只能在可用性和一致性两者间做出选择。CAP理论关注的是绝对情况下,在工程上,可用性和一致性并不是完全对立,我们关注的往往是如何在保持相对一致性的前提下,提高系统的可用性。

二、数据一致性模型

在互联网领域的绝大多数的场景,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。

对于一致性,可以分为从服务端和客户端两个不同的视角,即内部一致性和外部一致性。
没有全局时钟,绝对的内部一致性是没有意义的,一般来说,我们讨论的一致性都是外部一致性。外部一致性主要指的是多并发访问时更新过的数据如何获取的问题。

强一致性:
当更新操作完成之后,任何多个后续进程或者线程的访问都会返回最新的更新过的值。这种是对用户最友好的,就是用户上一次写什么,下一次就保证能读到什么。根据 CAP 理论,这种实现需要牺牲可用性。

弱一致性:
系统并不保证续进程或者线程的访问都会返回最新的更新过的值。用户读到某一操作对系统特定数据的更新需要一段时间,我们称这段时间为“不一致性窗口”。系统在数据写入成功之后,不承诺立即可以读到最新写入的值,也不会具体的承诺多久之后可以读到。

最终一致性:
是弱一致性的一种特例。系统保证在没有后续更新的前提下,系统最终返回上一次更新操作的值。在没有故障发生的前提下,不一致窗口的时间主要受通信延迟,系统负载和复制副本的个数影响。

最终一致性模型根据其提供的不同保证可以划分为更多的模型,包括因果一致性和读自写一致性等。

三、两阶段和三阶段提交

在分布式系统中,各个节点之间在物理上相互独立,通过网络进行沟通和协调。
典型的比如关系型数据库,由于存在事务机制,可以保证每个独立节点上的数据操作可以满足ACID。
但是,相互独立的节点之间无法准确的知道其他节点中的事务执行情况,所以两台机器理论上无法达到一致的状态。

如果想让分布式部署的多台机器中的数据保持一致性,那么就要保证在所有节点的数据写操作,要不全部都执行,要么全部的都不执行。
但是,一台机器在执行本地事务的时候无法知道其他机器中的本地事务的执行结果。所以节点并不知道本次事务到底应该commit还是 roolback。

所以实现分布式事务,需要让当前节点知道其他节点的任务执行状态。常规的解决办法就是引入一个“协调者”的组件来统一调度所有分布式节点的执行。著名的是二阶段提交协议(Two Phase Commitment Protocol)和三阶段提交协议(Three Phase Commitment Protocol)。

1.二阶段提交协议

Two Phase指的是Commit-request阶段Commit阶段。

  • 请求阶段
    在请求阶段,协调者将通知事务参与者准备提交或取消事务,然后进入表决过程。
    在表决过程中,参与者将告知协调者自己的决策:同意(事务参与者本地作业执行成功)或取消(本地作业执行故障)。

  • 提交阶段
    在该阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消。
    当且仅当所有的参与者同意提交事务协调者才通知所有的参与者提交事务,否则协调者将通知所有的参与者取消事务。参与者在接收到协调者发来的消息后将执行响应的操作。

 
 

可以看出,两阶段提交协议存在明显的问题:

  • 同步阻塞
    执行过程中,所有参与节点都是事务独占状态,当参与者占有公共资源时,第三方节点访问公共资源被阻塞。

  • 单点问题
    一旦协调者发生故障,参与者会一直阻塞下去。

  • 数据不一致性
    在第二阶段中,假设协调者发出了事务commit的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行commit,其余的参与者没有收到通知一直处于阻塞状态,这段时间就产生了数据的不一致性。

2.三阶段提交协议

Three Phase分别为CanCommit、PreCommit、DoCommit。

 

三阶段提交针对两阶段提交做了改进:

  • 引入超时机制。在2PC中,只有协调者拥有超时机制,3PC同时在协调者和参与者中都引入超时机制。
  • 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。

四、Paxos算法的提出

二阶段提交还是三阶段提交都无法很好的解决分布式的一致性问题,直到Paxos算法的提出,Paxos协议由Leslie Lamport最早在1990年提出,目前已经成为应用最广的分布式一致性算法。

Google Chubby的作者Mike Burrows说过这个世界上只有一种一致性算法,那就是Paxos,其它的算法都是残次品。

1.节点角色

Paxos 协议中,有三类节点:

  • Proposer:提案者

Proposer 可以有多个,Proposer 提出议案(value)。所谓 value,在工程中可以是任何操作,例如“修改某个变量的值为某个值”、“设置当前 primary 为某个节点”等等。Paxos 协议中统一将这些操作抽象为 value。
不同的 Proposer 可以提出不同的甚至矛盾的 value,例如某个 Proposer 提议“将变量 X 设置为 1”,另一个 Proposer 提议“将变量 X 设置为 2”,但对同一轮 Paxos 过程,最多只有一个 value 被批准。

  • Acceptor:批准者

Acceptor 有 N 个,Proposer 提出的 value 必须获得超过半数(N/2+1)的
Acceptor 批准后才能通过。Acceptor 之间完全对等独立。

  • Learner:学习者

Learner 学习被批准的 value。所谓学习就是通过读取各个 Proposer 对 value 的选择结果,如果某个 value 被超过半数 Proposer 通过,则 Learner 学习到了这个 value。

这里类似 Quorum 议会机制,某个 value 需要获得 W=N/2 + 1 的 Acceptor 批准,Learner 需要至少读取 N/2+1 个 Accpetor,至多读取 N 个 Acceptor 的结果后,能学习到一个通过的 value。

2.约束条件

上述三类角色只是逻辑上的划分,实践中一个节点可以同时充当这三类角色。有些文章会添加一个Client角色,作为产生议题者,实际不参与选举过程。

Paxos中 proposer 和 acceptor 是算法的核心角色,paxos 描述的就是在一个由多个 proposer 和多个 acceptor 构成的系统中,如何让多个 acceptor 针对 proposer 提出的多种提案达成一致的过程,而 learner 只是“学习”最终被批准的提案。

Paxos协议流程还需要满足几个约束条件:

  • Acceptor必须接受它收到的第一个提案;
  • 如果一个提案的v值被大多数Acceptor接受过,那后续的所有被接受的提案中也必须包含v值(v值可以理解为提案的内容,提案由一个或多个v和提案编号组成);
  • 如果某一轮 Paxos 协议批准了某个 value,则以后各轮 Paxos 只能批准这个value;

每轮 Paxos 协议分为准备阶段和批准阶段,在这两个阶段 Proposer 和 Acceptor 有各自的处理流程。

Proposer与Acceptor之间的交互主要有4类消息通信,如下图:

 
 

这4类消息对应于paxos算法的两个阶段4个过程:

  • Phase 1
    a) proposer向网络内超过半数的acceptor发送prepare消息
    b) acceptor正常情况下回复promise消息
  • Phase 2
    a) 在有足够多acceptor回复promise消息时,proposer发送accept消息
    b) 正常情况下acceptor回复accepted消息

3.选举过程

 
 
  • Phase 1 准备阶段

Proposer 生成全局唯一且递增的ProposalID,向 Paxos 集群的所有机器发送 Prepare请求,这里不携带value,只携带N即ProposalID 。

Acceptor 收到 Prepare请求 后,判断:收到的ProposalID 是否比之前已响应的所有提案的N大:
如果是,则:
(1) 在本地持久化 N,可记为Max_N。
(2) 回复请求,并带上已Accept的提案中N最大的value(若此时还没有已Accept的提案,则返回value为空)。
(3) 做出承诺:不会Accept任何小于Max_N的提案。

如果否:不回复或者回复Error。

  • Phase 2 选举阶段

P2a:Proposer 发送 Accept
经过一段时间后,Proposer 收集到一些 Prepare 回复,有下列几种情况:
(1) 回复数量 > 一半的Acceptor数量,且所有的回复的value都为空,则Porposer发出accept请求,并带上自己指定的value。
(2) 回复数量 > 一半的Acceptor数量,且有的回复value不为空,则Porposer发出accept请求,并带上回复中ProposalID最大的value(作为自己的提案内容)。
(3) 回复数量 <= 一半的Acceptor数量,则尝试更新生成更大的ProposalID,再转P1a执行。

P2b:Acceptor 应答 Accept
Accpetor 收到 Accpet请求 后,判断:
(1) 收到的N >= Max_N (一般情况下是 等于),则回复提交成功,并持久化N和value。
(2) 收到的N < Max_N,则不回复或者回复提交失败。

P2c: Proposer 统计投票
经过一段时间后,Proposer 收集到一些 Accept 回复提交成功,有几种情况:
(1) 回复数量 > 一半的Acceptor数量,则表示提交value成功。此时,可以发一个广播给所有Proposer、Learner,通知它们已commit的value。
(2) 回复数量 <= 一半的Acceptor数量,则 尝试 更新生成更大的 ProposalID,再转P1a执行。
(3) 收到一条提交失败的回复,则尝试更新生成更大的 ProposalID,再转P1a执行。

4.相关讨论

Paxos算法的核心思想:
(1)引入了多个Acceptor,单个Acceptor就类似2PC中协调者的单点问题,避免故障
(2)Proposer用更大ProposalID来抢占临时的访问权,可以对比2PC协议,防止其中一个Proposer崩溃宕机产生阻塞问题
(3)保证一个N值,只有一个Proposer能进行到第二阶段运行,Proposer按照ProposalID递增的顺序依次运行
(3) 新ProposalID的proposer比如认同前面提交的Value值,递增的ProposalID的Value是一个继承关系

为什么在Paxos运行过程中,半数以内的Acceptor失效都能运行?
(1) 如果半数以内的Acceptor失效时 还没确定最终的value,此时,所有Proposer会竞争 提案的权限,最终会有一个提案会 成功提交。之后,会有半过数的Acceptor以这个value提交成功。
(2) 如果半数以内的Acceptor失效时 已确定最终的value,此时,所有Proposer提交前 必须以 最终的value 提交,此值也可以被获取,并不再修改。

如何产生唯一的编号呢?
在《Paxos made simple》中提到的是让所有的Proposer都从不相交的数据集合中进行选择,例如系统有5个Proposer,则可为每一个Proposer分配一个标识j(0~4),则每一个proposer每次提出决议的编号可以为5*i + j(i可以用来表示提出议案的次数)。

推荐larmport和paxos相关的三篇论文:
The Part-Time Parliament
Paxos made simple
Fast Paxos

2PC/3PC和Paxos协议是经典的分布式协议,理解了它们以后,学习其他分布式协议会简单很多。

参考:
CAP theorem
浅谈分布式系统的基本问题:可用性与一致性
分布式系统入门到实战
图解分布式一致性协议Paxos
Paxos协议学习小结

从分布式一致性到共识机制(一)Paxos算法的更多相关文章

  1. 从分布式一致性到共识机制(二)Raft算法

    春秋五霸说开 春秋五霸,是指东周春秋时期相继称霸主的五个诸侯,“霸”,意为霸主,即是诸侯之领袖.典型的比如齐桓公,晋文公,春秋时期诸侯国的称霸,与今天要讨论的Raft算法很像. 一.更加直观的Raft ...

  2. 对标Eureka的AP一致性,Nacos如何实现Raft算法

    一.快速了解Raft算法 Raft 适用于一个管理日志一致性的协议,相比于 Paxos 协议 Raft 更易于理解和去实现它. 为了提高理解性,Raft 将一致性算法分为了几个部分,包括领导选取(le ...

  3. 分布式共识算法 (二) Paxos算法

    系列目录 分布式共识算法 (一) 背景 分布式共识算法 (二) Paxos算法 分布式共识算法 (三) Raft算法 分布式共识算法 (四) BTF算法 一.背景 1.1 命名 Paxos,最早是Le ...

  4. 【好】Paxos以及分布式一致性的学习

    Paxos,一言以蔽之,我们需要一种提交协议来确保分布式系统中的全局操作即使是在发生故障的情况下也能保证正确性. 跟拜占庭将军问题是不同的问题,虽然拜占庭也是Lamport提出的.拜占庭里面有叛徒,有 ...

  5. 分布式 一致性Paxos算法(转载)

    比较通俗易懂,可以入门,转载地址是http://www.cnblogs.com/linbingdong/p/6253479.html Paxos算法在分布式领域具有非常重要的地位.但是Paxos算法有 ...

  6. 分布式技术专题-分布式协议算法-带你彻底认识Paxos算法、Zab协议和Raft协议的原理和本质

    内容简介指南 Paxo算法指南 Zab算法指南 Raft算法指南 Paxo算法指南 Paxos算法的背景 [Paxos算法]是莱斯利·兰伯特(Leslie Lamport)1990年提出的一种基于消息 ...

  7. Paxos 算法 :消息传递一致性

    网络上有很多关于优秀的关于Paxos 算法的文章,我下面进行整理搜集一下: 分布式理论之一:Paxos算法的通俗理解 维基的简介:Paxos算法是莱斯利·兰伯特(Leslie Lamport,就是 L ...

  8. Paxos算法细节详解(一)--通过现实世界描述算法

    Paxos分析 最近研究paxos算法,看了许多相关的文章,概念还是很模糊,觉得还是没有掌握paxos算法的精髓,所以花了3天时间分析了libpaxos3的所有代码,此代码可以从https://bit ...

  9. Paxos算法细节详解(一)

    Paxos分析 最近研究paxos算法,看了许多相关的文章,概念还是很模糊,觉得还是没有掌握paxos算法的精髓,所以花了3天时间分析了libpaxos3的所有代码,此代码可以从https://bit ...

随机推荐

  1. 304和浏览器http缓存

    浏览器虽然发现了本地有该资源的缓存,但是不确定是否是最新的,于是想服务器询问,若服务器认为浏览器的缓存版本还可用,那么便会返回304. 浏览器缓存分为强缓存和协商缓存. 1.浏览器请求某资源,通过he ...

  2. js随机出现2个数字

    1和2 随机出现 <script type="text/javascript"> $(function(){ if (Math.random()>0.5) { c ...

  3. MySQL参数max_connect_errors分析释疑

      最近一MySQL服务器,由于一些特殊因素遇到"ERROR 1129 (00000): Host 'xxx' is blocked because of many connection e ...

  4. R︱并行计算以及提高运算效率的方式(parallel包、clusterExport函数、SupR包简介)

    要学的东西太多,无笔记不能学~~ 欢迎关注公众号,一起分享学习笔记,记录每一颗"贝壳"~ --------------------------- 终于开始攻克并行这一块了,有点小兴 ...

  5. 如何在WDM中使用xp系统的DMA用来处理数据

    最近做了一款pci的视频采集卡(H264压缩),由于数据传输量比较大,所有想采用dma来传输数据,刚开始感觉很简单,后来感觉还是困难重重. DMA 验证监控直接内存访问 (DMA) 的使用.随着 Wi ...

  6. eclipse生成【带有外部jar包】的java可执行jar包

    之前有写过一篇使用eclipse生成java可执行jar包,但是最近的一次使用中无论如何都不成功,当双击执行打成的jar时,弹出如下错误: could not find the main class: ...

  7. 蜻蜓FM 涉嫌诈骗投资人和广告主源代码剖析

    本文主要内容,引用自知乎的这篇文章:如何评价蜻蜓 FM 伪造用户活跃度等数据 感谢"左莫"."任正"等热心正义的网友--左莫,任正的最早的回答猜测已经被蜻蜓FM ...

  8. R语言︱常用统计方法包+机器学习包(名称、简介)

    一.一些函数包大汇总 转载于:http://www.dataguru.cn/thread-116761-1-1.html 时间上有点过期,下面的资料供大家参考基本的R包已经实现了传统多元统计的很多功能 ...

  9. Caused by: java.lang.ClassNotFoundException: org.springframework.context.ApplicationContextAware

    1.错误描述 usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonaming ] { -help ...

  10. 指针数组与带参main函数

    (一)指针数组 指针数组就是每一个元素存放一个地址,相当于一个指针变量.如:int *p[4]指针数组比较适合用来指向若干字符串,使得处理字符串更加灵活.例如,现在要将若干字符串按字母顺序由小到大输出 ...