1 分布式个人相关观点
- 分布式算法是分布式技术中的核心,因为分布式系统里,最重要的事情,就是如何选择或设计适合的算法,解决一致性和可用性相关的问题了。
- 方法得当,并不难学
- 大部分的努力程度还没到拼天赋的时候,这是我第一份工作我导师告诉我的
2 拜占庭将军问题
拜占庭将军问题(The Byzantine Generals Problem)提供了对分布式共识问题的一种情景化描述, 由Leslie Lamport等人在1982年首次发表. 论文同时提供了两种解决拜占庭将军问题的算法:
- 口信消息型解决方案(A solution with oral message);
- 签名消息型解决方案(A solution with signed message).
[拜占庭帝国(Byzantine Empire)]军队的几个师驻扎在敌城外, 每个师都由各自的将军指挥. 将军们只能通过信使相互沟通. 在观察敌情之后, 他们必须制定一个共同的行动计划, 如进攻(Attack) 或者撤退(Retreat) , 且只有当半数以上的将军共同发起进攻时才能取得胜利. 然而, 其中一些将军可能是叛徒, 试图阻止忠诚的将军达成一致的行动计划. 更糟糕的是, 负责消息传递的信使也可能是叛徒, 他们可能篡改或伪造消息, 也可能使得消息丢失.
为简化问题,以三将军问题为例进行说明. 当三个将军都忠诚时, 可以通过投票确定一致的行动方案,下图展示了一种场景, 即General A, B通过观察敌军军情并结合自身情况判断可以发起攻击, 而General C通过观察敌军军情并结合自身情况判断应当撤退. 最终三个将军经过投票表决得到结果为进攻:撤退=2:1, 所以将一同发起进攻取得胜利. 对于三个将军, 每个将军都能执行两种决策(进攻或撤退)的情况下, 共存在6中不同的场景, 下图是其中一种, 对于其他5中场景可简单地推得, 通过投票三个将军都将达成一致的行动计划.
当三将军有一个为叛徒,将有可能扰乱作战计划。下图展示了General C为叛徒的一种场景, 他给General A和General B发送了不同的消息, 在这种场景下General A通过投票得到进攻:撤退=1:2, 最终将作出撤退的行动计划; General B通过投票得到进攻:撤退=2:1, 最终将作出进攻的行动计划. 结果只有General B发起了进攻并战败.
事实上, 对于三个将军中存在一个叛徒的场景, 想要总能达到一致的行动方案是不可能的. 详细的证明可参看Leslie Lamport的论文. 此外, 论文中给出了一个更加普适的结论: 如果存在m个叛将, 那么至少需要3m+1个将军, 才能最终达到一致的行动方案.
2.1 解决方案
Leslie Lamport在论文中给出了两种拜占庭将军问题的解决方案, 即口信消息型解决方案(A solution with oral message)和签名消息型解决方案(A solution with signed message).
2.1.1 口信消息型解决方案
首先, 对于口信消息(Oral message)的定义如下:
- A1. 任何已经发送的消息都将被正确传达;
- A2. 消息的接收者知道是谁发送了消息;
- A3. 消息的缺席可以被检测.
基于口信消息的定义, 我们可以知道, 口信消息不能被篡改但是可以被伪造. 基于对图3场景的推导, 我们知道存在一个叛将时, 必须再增加3个忠将才能达到最终的行动一致. 为加深理解, 我们将利用3个忠将1个叛将的场景对口信消息型解决方案进行推导. 在口信消息型解决方案中, 首先发送消息的将军称为指挥官, 其余将军称为副官. 对于3忠1叛的场景需要进行两轮作战信息协商, 如果没有收到作战信息那么默认撤退. 图4是指挥官为忠将的场景, 在第一轮作战信息协商中, 指挥官向3位副官发送了进攻的消息; 在第二轮中, 三位副官再次进行作战信息协商, 由于General A, B为忠将, 因此他们根据指挥官的消息向另外两位副官发送了进攻的消息, 而General C为叛将, 为了扰乱作战计划, 他向另外两位副官发送了撤退的消息. 最终Commanding General, General A和B达成了一致的进攻计划, 可以取得胜利.
图5是指挥官为叛将的场景, 在第一轮作战信息协商中, 指挥官向General A, B发送了撤退的消息, 但是为了扰乱General C的决定向其发送了进攻的消息. 在第二轮中, 由于所有副官均为忠将, 因此都将来自指挥官的消息正确地发送给其余两位副官. 最终所有忠将都能达成一致撤退的计划.
如上所述, 对于口信消息型拜占庭将军问题, 如果叛将人数为m, 将军人数不少于3m+1, 那么最终能达成一致的行动计划. 值的注意的是, 在这个算法中, 叛将人数m是已知的, 且叛将人数m决定了递归的次数, 即叛将数m决定了进行作战信息协商的轮数, 如果存在m个叛将, 则需要进行m+1轮作战信息协商. 这也是上述存在1个叛将时需要进行两轮作战信息协商的原因.
2.1.2 签名消息型解决方案
同样, 对签名消息的定义是在口信消息定义的基础上增加了如下两条:
- A4. 忠诚将军的签名无法伪造,而且对他签名消息的内容进行任何更改都会被发现;
- A5. 任何人都能验证将军签名的真伪.
基于签名消息的定义, 我们可以知道, 签名消息无法被伪造或者篡改. 为了深入理解签名消息型解决方案, 我们同样以3三将军问题为例进行推导. 图6是忠将率先发起作战协商的场景, General A率先向General B, C发送了进攻消息, 一旦叛将General C篡改了来自General A的消息, 那么General B将将发现作战信息被General C篡改, General B将执行General A发送的消息.
图7是叛将率先发起作战协商的场景, 叛将General C率先发送了误导的作战信息, 那么General A, B将发现General C发送的作战信息不一致, 因此判定其为叛将. 可对其进行处理后再进行作战信息协商.
签名消息型解决方案可以处理任何数量叛将的场景.
2.2 拜占庭问题总结
在分布式系统领域, 拜占庭将军问题中的角色与计算机世界的对应关系如下:
- 将军, 对应计算机节点;
- 忠诚的将军, 对应运行良好的计算机节点;
- 叛变的将军, 被非法控制的计算机节点;
- 信使被杀, 通信故障使得消息丢失;
- 信使被间谍替换, 通信被攻击, 攻击者篡改或伪造信息.
如上文所述, 拜占庭将军问题提供了对分布式共识问题的一种情景化描述, 是分布式系统领域最复杂的模型. 此外, 它也为我们理解和分类现有的众多分布式一致性协议和算法提供了框架. 现有的分布式一致性协议和算法主要可分为两类:
- 一类是故障容错算法(Crash Fault Tolerance, CFT) , 即非拜占庭容错算法, 解决的是分布式系统中存在故障, 但不存在恶意攻击的场景下的共识问题. 也就是说, 在该场景下可能存在消息丢失, 消息重复, 但不存在消息被篡改或伪造的场景. 一般用于局域网场景下的分布式系统, 如分布式数据库. 属于此类的常见算法有Paxos算法, Raft算法, ZAB协议等.
- 一类是拜占庭容错算法, 可以解决分布式系统中既存在故障, 又存在恶意攻击场景下的共识问题. 一般用于互联网场景下的分布式系统, 如在数字货币的区块链技术中. 属于此类的常见算法有PBFT算法, PoW算法.
2.3 论文地址
论文 The Byzantine Generals Problem 1982 Leslie Lamport
3 CAP理论 分布式系统的PH试纸
CAP 理论对分布式系统的特性做了高度抽象,形成了三个指标:
- 一致性(Consistency)
- 可用性(Availability)
- 分区容错性(Partition Tolerance)
3.1 一致性
一致性说的是客户端的每次读操作,不管访问哪个节点,要么读到的都是同一份最新的数据,要么读取失败。你可以把一致性看作是分布式系统对访问本系统的客户端的一种承诺:不管你访问哪个节点,要么我给你返回的都是绝对一致的数据,要么你都读取失败。你可以看到,一致性强调的不是数据完整,而是各节点间的数据一致。
图中的例子原始两个节点的kv存储的都是 x=1,当客户端向节点1发送写x=2的1请求,节点1会向其他节点更新数据使得保持一致然后才返回客户端写成功的消息。一致性这个指标,描述的是分布式系统非常重要的一个特性,强调的是数据的一致。也就是说,在客户端看来,集群和单机在数据一致性上是一样的。
3.2 可用性
不过集群毕竟不是单机,当发生分区故障的时候,有时不能仅仅因为节点间出现了通讯问题,节点中的数据会不一致,就拒绝写入新数据,之后在客户端查询数据时,就一直返回给客户端出错信息。
这个时候,我们就需要牺牲数据的一致性,每个节点使用本地数据来响应客户端请求,来保证服务可用,这就是我要说的另外一个指标,可用性。
可用性说的是任何来自客户端的请求,不管访问哪个节点,都能得到响应数据,但不保证是同一份最新数据。你也可以把可用性看作是分布式系统对访问本系统的客户端的另外一种承诺:我尽力给你返回数据,不会不响应你,但是我不保证每个节点给你的数据都是最新的。这个指标强调的是服务可用,但不保证数据的一致。
3.3 分区容错性
最后的分区容错性说的是,当节点间出现任意数量的消息丢失或高延迟的时候,系统仍然可以继续提供服务。也就是说,分布式系统在告诉访问本系统的客户端:不管我的内部出现什么样的数据同步问题,我会一直运行,提供服务。这个指标,强调的是集群对分区故障的容错能力。
来看下面的图,当节点 1 和节点 2 通信出问题的时候,如果系统仍能提供服务,那么,2个节点是满足分区容错性的。
因为分布式系统与单机系统不同,它涉及到多节点间的通讯和交互,节点间的分区故障是必然发生的,所以在分布式系统中分区容错性是必须要考虑的。
CAP 不能三角最初是埃里克·布鲁尔(Eric Brewer)基于自己的工程实践,提出的一个猜想,后被赛斯·吉尔伯特(Seth Gilbert)和南希·林奇(Nancy Lynch)证明,证明过程可以参考论文《Brewer’s conjecture and the feasibility of consistent, available,partition-tolerant web services》,我们记住结论就好了。
3.4 CAP的应用
只要有网络交互就一定会有延迟和数据丢失,而这种状况我们必须接受,还必须保证系统不能挂掉。所以就像我上面提到的,节点间的分区故障是必然发生的。也就是说,分区容错性(P)是前提,是必须要保证的。
舍弃P就意味着舍弃分布式系统
- CA 模型,在分布式系统中不存在。因为舍弃 P,意味着舍弃分布式系统,就比如单机版关系型数据库 MySQL,如果 MySQL 要考虑主备或集群部署时,它必须考虑 P。
- CP 模型,采用 CP 模型的分布式系统,一旦因为消息丢失、延迟过高发生了网络分区,就影响用户的体验和业务的可用性。因为为了防止数据不一致,集群将拒绝新数据的写入,典型的应用是Kafka、 ZooKeeper,Etcd 和 HBase。
- AP 模型, 用 AP 模型的分布式系统,实现了服务的高可用。用户访问系统的时候,都能得到响应数据,不会出现响应错误,但当出现分区故障时,相同的读操作,访问不同的节点,得到响应数据可能不一样。典型应用就比如 Cassandra 和 DynamoDB。