系统里数据又“打架”了?让“少数服从多数”来终结这场混乱!
Quorum(法定人数/多数派)机制由 David K. Gifford 于 1979 年提出,是分布式系统中用于在副本间实现不同级别数据一致性与可用性的核心方法。其设计思想借鉴了数学中的鸽巢原理(Pigeonhole Principle):若将 N+1 个物体放入 N 个鸽巢,则至少有一个鸽巢包含两个或更多物体。在 Quorum 机制中,这被巧妙地应用于确保读写操作能在副本间“相遇”。
Quorum 机制包含三个要素 1)
N (Number of Replicas): 副本总数,也称复制因子(Replication Factor)。表示集群中同一份数据(或数据段)有多少个副本。从图中可以看到,在这个三节点的集群中,DATA-1 有 2 个副本,DATA-2 有 3 个副本,DATA-3 有 1 个副本。也就是说,副本数可以不等于节点数,不同的数据可以有不同的副本数。

2)W (Write Quorum):写一致性级别(Write Consistency Level),表示一个写操作需要成功更新至少 W 个副本后,才能被确认为成功。从图中可以看到,DATA-2 的写副本数为 2,也就说,对 DATA-2 执行写操作时,完成了 2 个副本的更新(比如节点 A、C),才完成写操作。

3)R (Read Quorum):读一致性级别(Read Consistency Level),表示一个读操作需要从至少 R 个副本中读取数据后,才能综合结果并返回给客户端。从图中可以看到,DATA-2 的读副本数为 2。也就是说,客户端读取 DATA-2 的数据时,需要读取 2 个副本中的数据,然后返回最新的那份数据。

Quorum 机制,主要思想为多数派思想,所谓多数即超过半数。现考虑这样一个场景:假设系统中某数据有 N 个副本,对于写操作,要求至少要在 W 个副本上更新成功后,才认为此次写操作成功;对于读操作,至少读取 R 个副本才认为此次读操作成功。因此为了保证读操作每次都能读取到最新写操作成功的数据,Quorum 要求 W+R>N,即写入 W 个副本与读取 R 个副本之间有重叠的副本。

NWR 组合
N、W、R 值的不同组合,会产生不同的一致性效果。1)W + R > N(强一致性配置):整个系统能保证强一致性,一定能返回更新后的那份数据。2)W + R < N (弱一致性/最终一致性配置):如果 W+R ≤ N,则读集和写集可能没有交集,读操作可能无法读到最新的已提交写,只能保证最终一致性。这种配置在某些对一致性要求不高但对可用性和分区容忍性要求极高的场景(如某些 NoSQL 数据库的特定配置)中可见。在 W + R > N 保证了强一致性的情况下,并且 N 一经固定,那么会有三种情况。1)W = 1,R = N(写优化/读悲观):写操作只需成功写入一个副本即可确认。但读操作必须读取所有 N 个副本来确定哪个是最新版本。这对写请求友好,但读性能和可用性较差。2)R = 1,W = N(读优化/写悲观):读操作只需访问任意一个副本即可获得最新数据,因为写操作必须成功写入所有 N 个副本。这对读请求性能友好,但写操作的可用性和延迟会受最慢副本的影响,容错性较低(任何一个副本写入失败都可能导致整个写操作失败)。3)W = Q, R = Q, where Q = floor(N/2) + 1(多数派读写/均衡配置):这是最常见的配置,例如 N=3 时,W=2, R=2。写操作需要写入超过半数的副本,读操作也需要读取超过半数的副本。在可用性(能容忍 floor(N/2) 个节点故障)和性能之间取得了较好的平衡。
Quorum 机制应用
假设系统中副本数 N=5,W=3,R=3,令 vn 表示第 n 次写操作写入的数据。初始状态 5 个副本数据分别为(v0 v0 v0 v0 v0),版本号为 0。现在第一次写操作在 3 个副本上更新成功,数据变为(v1 v1 v1 v0 v0),版本号为 1,即认为写操作成功。接下来读操作读取 3 个副本的数据时,一定可以读取到第一次写操作写入的数据 v1。写操作成功后,可继续将 v1 同步到剩余的 v0,而不需要让客户端知道。然而仅仅依靠上述 Quorum 机制并不能保证正常的读写服务。如当第一次写操作只在两个副本上更新成功,数据变为(v1 v1 v0 v0 v0)时,写操作失败。接下来的读操作可能读取到(v1 v1 v0)或者(v1 v0 v0),依旧可以读取到 v1。即仅仅通过一次读操作可能读取到第 n 次写操作写入的数据 vn,但不能确保 vn 已经提交。

假设在读取到(v1 v1 v0)时,继续读取剩余的副本,若读到剩余两个副本为(v1 v0)或(v1 v1),则 v1 是最新的已提交的副本;若读到剩余的两个副本为(v0 v0)时,则 v0 是最新成功提交的版本;若读取后续两个副本有任一超时或失败,则无法判断哪个版本是最新的成功提交的版本。
基于 Quorum 机制选择 Leader 节点
基于 Quorum 机制选取 Leader 节点时,中心节点首先读取 R 个副本,选择 R 个副本中版本号最高的副本作为新 Leader 节点,新选出的 Leader 节点不能立即提供服务,还需要至少与 W 个副本完成同步后,才能提供服务。在 N=5,W=3,R=3 的系统中,某时刻副本最大版本号为(v2 v2 v1 v1 v1),此时 v1 是系统的最新的成功提交的数据,v2 是一个处于中间状态的未成功提交的数据。假设此刻原 Leader 副本异常,中心节点进行 Leader 切换工作。这类“中间态”数据究竟作为“脏数据”被删除,还是作为新的数据被同步后成为生效的数据,完全取决于这个数据能否参与新 Leader 的选举。下面分别分析这两种情况。

若中心节点与其他三个副本通信成功,读取到的版本号为(v2 v1 v1),则选取版本号为 v2 的副本作为新的 Leader,之后,一旦新 Leader 与其他二个副本完成数据同步,则符合 v2 的副本个数达到 W 个,成为最新的成功提交的副本,新 Leader 可以提供正常的读写服务。

若中心节点与其中三个副本通信成功,读取到的版本号为(v1 v1 v1),则任选一个副本作为 Leader,新 Leader 以 v1 作为最新的成功提交的版本并与其他副本同步,当与第 1、第 2 个副本同步数据时,由于第 1、第 2 个副本版本号大于 Leader,属于脏数据。对于脏数据可以简单直接丢弃,也可以基于 undo 日志方式进行回滚。新 Leader 也有可能与后两个副本完成同步后就提供数据服务,随后自身版本号也更新到 v2,如果系统不能保证之后的 v2 与之前的 v2 完全一样,则新 Leader 在与第 1、2 个副本同步数据时不但要比较数据版本号还需要比较更新操作的具体内容是否一样。
未完待续
很高兴与你相遇!如果你喜欢本文内容,记得关注哦!!!
版权声明: 本文为 InfoQ 作者【poemyang】的原创文章。
原文链接:【http://xie.infoq.cn/article/33add754e9b5f54f536a18c9f】。文章转载请联系作者。
评论