——本文是作者自己原创编写的电子书中的部分章节,在此分享给各位优快云的读者。
接上篇
3.3 共识
一些资料会把「共识」和「一致性」混在一起不做区分,这二者在一些场景也确实可以不作区分,不过实际上它们是有强关联的两个不同概念。一致性是说所有节点的数据在某一个时刻的是否一致的状态,而共识是为了保证某一笔交易在所有节点上达成一致结果的过程。换句话说,共识会部分地解决一致性问题。
有效的共识需要具备以下特性:
- 认同性(Agreement):所有非故障节点都认同一次共识的结果;
- 合法性(Validity):共识的结果必须是参与共识的节点提出的;
- 可终止性(Termination):共识的过程在一定时间内结束,而不会无休止地执行下去。
在一个理想的网络环境中要实现这样的共识,看起来并不难。但是当考虑到网络异常、节点故障等情况时,系统还能针对每一笔提议达成共识并不是一件很容易的事情。
同步系统 VS 异步系统
根据是否存在全局时钟,可以将分布式系统分为\textbf{同步系统}和\textbf{异步系统}。相应的,分布式系统的共识算法也有同步共识算法和异步共识算法。
满足下列三个条件的系统是同步系统,否则就是异步系统:
- 各节点的时钟差有限;
- 网络传输延迟有限;
- 节点处理时间有限。
同步系统的共识问题显然要比异步系统更容易解决,在异步系统中能正常运行的共识,到同构的同步系统中也能正常运行,但反过来却不一定成立。
随着分布式技术的发展,很快就出现了一些可行的同步系统的共识算法,但是却一直没有公认可行的异步系统的共识算法,很多人曾试图挑战这一难题但都失败了,直到 FLP 原理横空出世。
FLP 原理
1985年,Michael J ⋅ \cdot ⋅ Fisher,Nany A ⋅ \cdot ⋅ Lynch 和 Michael S ⋅ \cdot ⋅ Paterson 在 ACM PODC 上发表了一篇论文 《Impossibility of distributed consensus with one faulty process》,提出了 FLP 原理(见图\ref{fig:ds_005}),并于 2001 年获得了 Dijkstra 奖。
FLP 原理的结论简洁而震撼:在异步网络中,即使只有一个进程可能失败,也没有任何算法保证其他正常进程能达成共识。
FLP 原理在分布式领域占据着如此重要的位置,被称为分布式领域的测不准原理。它的伟大之处在于为共识算法的设计画出了十分明确的边界:只能为同步系统设计有容错能力的共识算法。
值得强调的是,三个人中的 Nany Lynch 是一位分布式领域的杰出的女性科学家,本章接下来还会反复提到这个名字。
CAP定理
FLP 为共识算法画出了极其重要的设计边界,但并不意味着在这个边界之内就一定是畅通无阻的。当时分布式领域的科学家们受数据库事务的特性 ACID的影响,认为 Consistency 应该是分布式系统必须满足的基本特性,在这一点上的任何妥协都是不可接受的。这种固步自封的思维严重限制了共识算法的设计和发展。
2000年7月,加州大学伯克利分校的计算机科学家 Eric Brewer 在分布式计算原理研讨会(PODC)上提出了 CAP猜想:一个分布式系统,不可能同时满足下面三个条件:
- 强一致性(Strong Consistency):对于一个写操作,如果写成功,那么之后的读请求都读到该值; 如果写失败,那么之后的读操作都不能读到该值;
- 可用性(Availability):失效节点不会影响到其他正常节点;
- 分区容忍(Partition tolerance):当出现网络分区时,系统整体仍然能提供正常服务。
CAP(见图\ref{fig:ds_006})最开始只是Eric Brewer的一个猜想,直到2002年,麻省理工学院的 Seth Gilbert 和 Nancy Lynch(就是FLP里面的那个L – Nancy Lynch)给出了理论证明,把CAP从猜想升级成了一个定理。
CAP通过把 Availability 和 Partition tolerance 提到和 Consistency并列的高度,开阔了分布式领域的科学家们的思路:设计分布式系统应该是 CAP 三选二,而不是抓着 C 不放然后死磕 AP,最后陷入迷茫。
但实际上,很多人都可能会奇怪:我们日常使用的一些分布式系统类的工具,好像 C、A、P 都可以满足,没觉得什么时候不能用。这里需要解释一下,虽然 CAP 理论上是不可能的,但是通过在一定条件下降低 C 或者 A 的服务标准,在工程实现中是可以搞出 C、A、P 三个指标均满足实际需求的分布式系统的。
写到这里想到以前在微软招实习生的时候,偶尔会有学生说我希望去微软亚洲研究院,而不是工程院,因为做工程太 low 了。
有时我会跟他讲:做工程未必比研究容易,大家在学校都做过科研,做科研的时候总会假设当满足条件 A、B、C 的时候,然后去观察状态 D 的变化…
但做工程的时候往往是没有任何假设的,无论出现任何问题,系统都要在设计的时间内给出预期的、可接受的结果,这是工程师要做的事情。因此,尽管科学家说 CAP 只能三选二,但工程师仍然能做出 CAP 均满足实际需求的系统,这是工程师的伟大使命。
3.3.1 共识算法的分类
从技术上来讲,站在不同的角度,可以对共识算法的做不同的分类(如果更细致地划分,共识机制和共识算法也是可以分开讨论的)。
比如:
- 从同步/异步的角度来看,可以分为同步共识算法、异步共识算法;
- 从容错类型的角度做分类的话,可以把共识分为 CFT 、 BFT;
- 从写操作发起方来分类的话,可以分为单主共识和多主共识。
关于同步系统和异步系统在前面已经做了简单的介绍。区块链领域的程序员很少关注纯异步系统的共识,因为纯异步系统的存在条件在现实中还是很容易打破的,没有必要为难自己非要搞一个异步系统的共识出来。
受bit–coin的影响,早期的区块链研发普遍是用 CFT/BFT 来给共识算法分类的,这本身没有问题。但如果不了解「单主/多主」这些分布式系统的基础知识的话,看待共识问题的时候就少了一个维度,所以下文也还是会很简要的介绍一下。
下面来看一下「 CFT & \& & BFT 」和「单主共识 & \& & 多主共识」。
CFT & \& & BFT
选好了 CAP 策略之后,在选择/设计共识算法的时候,还要注意首先选择哪一类共识算法。根据网络节点的特征,可以分为 CFT 和 BFT:
- CFT 是可以容忍节点宕机的共识算法,比如 Paxos 、 Raft;
- BFT 不仅可以容忍节点宕机,而且可以容忍恶意节点捣乱,在恶意节点不超过一定比例的情况下可以保证仍然能达成共识。
这么看起来,BFT 可比 CFT 好多了,是不是直接搞 BFT 就可以了呢?当然不是这样,BFT 在引入强大的容错能力的同时,也带来各种问题,包括可能存在的性能问题(还可能有耗电量的问题),所以工程应用中并不是一味追求 BFT。选择的标准也很简单:网络中是不是存在拜占庭节点(恶意节点); 如果存在恶意节点的话就要选 BFT,否则就不推荐选 BFT。
对于公链来说,无法控制加入的节点,也无法拒绝恶意节点的加入,所以一般选择 BFT类算法。而 BFT 又分为:
- 确定性算法,如 PBFT;
- 非确定性算法, 如 PoW。
对于联盟链来说,所有的节点都是授权加入的,不存在故意引入恶意节点的问题,所以一般选择使用 CFT 类共识算法。但这也不是绝对的,比如 Fabric 作为典型的联盟链,v0.6 版本就使用了 BFT 类共识,下文会有详细的介绍。
单主共识 & \& & 多主共识
如果同一时刻只有一个节点负责写操作,就是单主共识; 反之,如果存在多个节点同时做写操作的情况,就是多主共识。
单主共识要求所有节点的写操作都提交到唯一的(但不需要是固定的)主节点,由主节点负责控制写操作,可以保证写操作的顺序一致性的,因此主节点也被称为定序器。多主共识允许多个节点负责写操作,显然无法保证写操作的顺序一致性,只是保证最终一致性。
直观的来理解,由于多个节点可以负责写操作,多主共识应该会提供比单主共识更好的性能,但这其实是一个误解。比如,著名的 PoW 就是典型的多主共识:任意一个节点都可以拥有记账权,但通常情况下 PoW 共识的性能并不是特别好。
前面简要介绍了一些关于共识的基础知识,下面来看一些具体的共识算法。
待续……