Quorum 机制

236 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情

先做这样的约定:更新操作(write)是一系列顺序的过程,通过其他机制确定更新操 作的顺序(例如primary-secondary 架构中由primary 决定顺序),每个更新操作记 为wi, i 为更新操作单调递增的序号,每个wi 执行成功后副本数据都发生变化,称为 不同的数据版本,记 作vi。假设每个副本都保存了历史上所有版本的数据。

write-all-read-one

Write-all-read-one(简称WARO)是一种最简单的副本控制规则,顾名思义即在更 新时写所有的副本,只有在所有的副本上更新成功,才认为更新成功,从而保证所有 的副本一致,这样在读取数据时可以读任一副本上的数据。

由于更新操作需要在所有的N 个副本上都成功,更新操作才能成 功,所以一旦有一 个副本异常,更新操作失败,更新服务不可用。对于更新服务,虽然有N 个副本, 但系统无法容忍任何一个副本异常。另一方面,N 个副本中只要有一个副本正常,系 统就可以提供读服务。对于读服务而言,当有N 个副本时,系统可以容忍N-1 个副本 异常。从上述分析可以发现WARO 读服务的可用性较高,但更新服务的可用性不 高,甚至虽然使用了副本,但更新服务的可用性等效于没有副本。

Quorum 定义

在Quorum 机制下,当某次更新操作wi 一旦在所有N 个副本中的W 个副本上都成 功,则就称 该更新操作为“成功提交的更新操作”,称对应的数据为“成功提交的数 据”。令R>N-W,由于更新 操作wi 仅在W 个副本上成功,所以在读取数据时,最 多需要读取R 个副本则一定能读到wi 更新后 的数据vi 。如果某次更新wi 在W 个副本 上成功,由于W+R>N,任意R 个副本组成的集合一定与 成功的W个副本组成的集合有交集,所以读取R 个副本一定能读到wi 更新后的数据vi。如图 2-10, Quorum 机 制的原理可以文森图表示。

某系统有5 个副本,W=3,R=3,最初5 个副本的数据一致,都是v1,某次更新操作 w2 在前3 副本上成功,副本情况变成(v2 v2 v2 v1 v1)。此时,任意3 个副本组成 的集合中一定包括 v2。在上述定义中,令W=N,R=1,就得到WARO,即WARO 是Quorum 机制的一种特例。与分析WARO 相似,分析Quorum 机制的可用性。限 制Quorum 参数为W+R=N+1。由于更新 操作需要在W 个副本上都成功,更新操作 才能成功,所以一旦N-W+1 个副本异常,更新操作始终无法在W 个副本上成功,更 新服务不可用。另一方面,一旦N-R+1 个副本异常,则无法保证一定可以读到与W 个副本有交集的副本集合,则读服务的一致性下降。

再次强调:仅仅依赖quorum 机制是无法保证强一致性的。因为仅有quorum 机制时 无法确定最新已成功提交的版本号,除非将最新已提交的版本号作为元数据由特定的 元数据服务器或元数据集群管理,否则很难确定最新成功提交的版本号。在下一节 中,将讨论在哪些情况下,可以仅仅 通过quorum 机制来确定最新成功提交的版本号。

Quorum 机制的三个系统参数N、W、R 控制了系统的可用性,也是系统对用户的服 务承诺:数据最多有N 个副本,但数据更新成功W 个副本即返回用户成功。对于一 致性要求较高的Quorum 系统,系统还应该承诺任何时候不读取未成功提交的数据, 即读取到的数据都是曾经在W 个副本上成功的数据。

读取最新成功提交的数据

Quorum 机制只需成功更新N 个副本中的W 个,在读取R 个副本时,一定可以读到 最新的成功提交的数据。但由于有不成功的更新情况存在,仅仅读取R 个副本却不一 定能确定哪个版本的数据 是最新的已提交的数据。对于一个强一致性Quorum 系 统,若存在个数据少于W 个,假设为X 个,则继续读取其他副本,直若成功读取到W 个 该版本的副本,则该数据为最新的成功提交的数据;如果在所有副本中该数据的个数肯定不满 足W 个,则R 中版本号第二大的为最新的成功提交的副本。例:在读取 到(v2 v1 v1)时,继续读取剩余的副本,若读到剩余两个副本 为(v2 v2)则v2 是 最新的已提交的副本;若读到剩余的两个副本为(v2 v1)或(v1 v1)则v1 是最新 成功提交的版本;若读取后续两个副本有任一超时或失败,则无法判断哪个版本是最 新的成功提交的版本。

可以看出,在单纯使用Quorum 机制时,若要确定最新的成功提交的版本,最多需要 读取R+ (W-R-1)=N 个副本,当出现任一副本异常时,读最新的成功提交的版本 这一功能都有可能不可用。实际工程中,应该尽量通过其他技术手段,回避通过 Quorum 机制读取最新的成功提交的版本。例如,当quorum 机制与primarysecondary 控制协议结合使用时,可以通过读取primary 的方式读取到最新的已提交 的数据。

基于Quorum 机制选择primary副本

读取数据时依照一致性要求的不同可以有不同的做法:如果需要强一致性的立刻读取 到最新的成功提交的数据,则可以简单的只读取primary 副本上的数据即可,也可以 通过上节的方式读取;如果需要会话一致性,则可以根据之前已经读到的数据版本号 在各个副本上进行选择性读取;如果只需要弱一致性,则可以选择任意副本读取。

在primary-secondary 协议中,当primary 异常时,需要选择出一个新的primary, 之后secondary 副本与primary 同步数据。通常情况下,选择新的primary 的工作是 由某一中心节点完成的,在引入 quorum 机制后,常用的primary 选择方式与读取 数据的方式类似,即中心节点读取R 个副本,选择 R 个副本中版本号最高的副本作为 新的primary。新primary 与至少W 个副本完成数据同步后作为新的primary 提供读 写服务。首先,R 个副本中版本号最高的副本一定蕴含了最新的成功提交的数据。再 者,虽然不能确定最高版本号的数是一个成功提交的数据,但新的primary 在随后与 secondary 同 步数据,使得该版本的副本个数达到W,从而使得该版本的数据成为 成功提交的数据。

例:在N=5,W=3,R=3 的系统中,某时刻副本最大版本号为(v2 v2 v1 v1 v1),此时v1 是系统的最新的成功提交的数据,v2 是一个处于中间状态的未成功提交的数据。假设此刻原primary 副本异常,中心节点进行primary 切换工作。这 类“中间态”数据究竟作为“脏数据”被删除,还是作为新的数据被同步后成为生效 的数据,完全取决于这个数据能否参与新primary 的选举。下面分别分析这两种情 况。

第一、如图 2-12,若中心节点与其中3 个副本通信成功,读取到的版本号为(v1 v1 v1),则任 选一个副本作为primary,新primary 以v1 作为最新的成功提交的版本 并与其他副本同步,当与第1、第2 个副本同步数据时,由于第1、第2 个副本版本号 大于primary,属于脏数据,可以按照2.2.2.4 节中介绍的处理脏数据的方式解决。实 践中,新primary 也有可能与后两个副本完成同步后就提供数据服务,随后自身版本 号也更新到v2,如果系统不能保证之后的v2 与之前的v2 完全一样,则新 primary 在 与第1、2 个副本同步数据时不但要比较数据版本号还需要比较更新操作的具体内容 是否一样。

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