分布式系统之Quorum机制

《分布式系统之中心副本控制协议(Primary-secondary协议)》 中略微提及到了Quorum,但没有进行详细的阐述,这篇文章将带你走进Quorum.

1. Quorum介绍

首先,先认识发音和基本含义,英[ˈkwɔ:rəm], 美[ˈkwɔrəm, ˈkwor-],n. 法定人数.
在介绍Quorum之前,先对一些概念进行定义:系统中的更新操作定义为 w i i 为更新操作单调递增序号。每个 w i 执行成功后副本数据的版本记为 v i . 假设每个副本保存了历史上所有版本的数据。

1.1 Write all read one(WARO)

WARO的意思是:在更新时写所有副本,只有在所有副本中更新成功才算成功 ,保证了所有副本中数据的一致性,读取时可以读任意副本数据。

很显然,这种机制在工程实践中并没有那么好的可用性,为什么呢?

更新操作需要在N个节点上都更新成功才算真正成功,只要存在一个节点异常,则更新服务不可用。更新操作无法容忍任一节点的异常。
但是WARO读服务却有着较高的容错能力,可以容忍N-1个副本异常。读服务有较高的可用性。

WARO机制同时需要原服务器记录数据版本信息,从而达到数据的强一致性。WARO牺牲了更新服务的可用性,最大程度地增强了读服务的可用性。

1.2 Quorum机制

对WARO条件进行松弛,对读写服务可用性做折中。

Quorum机制:当更新操作 w i 在N个节点中的W个节点上更新成功,称之为“成功提交的更新操作”。由于更新操作 w i 在W个节点上更新成功,所以在读数据时,令 R > N W ,最多读取R个节点副本一定能读到更新操作 w i 更新后的数据 v i

说白了,在更新操作 w i 成功提交后,R+W>N,那么读取的R个节点一定会与更新成功的W个节点有交集。此时你是否想到了鸽巢原理。

当W=N,R=1时,就可以得到WARO机制,所以WARO是Quorum的特例。

那WARO的可用性如何呢?

限制Quorum参数W+R=N+1.
更新操作在W个节点上更新成功,当N-W+1个节点异常时,更新操作不可用。
当N-R+1个节点异常,无法保证读取内容与W个副本有交集,读服务一致性降低。

注意:仅仅依赖Quorum机制无法保证强一致性。因为仅有Quorum机制时无法确定最新已成功提交的数据。那如何能够读到最新提交数据呢?

1.3 增强Quorum机制(读取最新成功提交数据)

为了能够保证系统的强一致性,系统应该返回最新成功提交数据,需要对Quorum机制进行条件增强。

1. 更新操作严格递增(数据版本号严格递增),只有前一个更新操作成功提交才可以允许后一个更新操作提交
2. 读取R个节点,取得最高版本号数据
    2.1. 在读取的R个节点中,存在W个节点有最高版本数据,则该数据为最新成功提交的数据
    2.2. 若存在小于W个节点有最高版本数据,则继续读取剩余节点,直至读到W个节点存在最高版本数据为止;
         如果所有节点读取完毕不满足W个,则版本号第二大的数据为最新成功提交的数据(此时可能处于更新提交的中间态)

从上述增强Quorum机制可得,若需确定最新成功提交的数据,最多需要读取N个节点。

当出现任一节点异常时,可能无法读到最新成功提交的数据。

例如:共有5个节点,W=3,R=3,已读取3个节点数据版本为( v 2 , v 1 , v 1 )。
1. 若剩余2个节点读到( v 2 v 2 ),则 v 2 为最新成功提交的数据版本;
2. 若剩余2个读到( v 2 v 1 )或( v 1 v 1 ),则 v 1 为最新成功提交的数据版本;
3. 若剩余2个节点中,读取到1个节点数据版本为( v 2 ),另一个节点异常,则无法判断哪个版本是最新成功提交的数据。

在工程实践中,节点异常是经常会出现的情况,需要尽量回避通过Quorum机制来读取最新成功提交数据。

2. 基于Quorum机制选择primary

当Quorum机制与Primary-Secondary协议结合时,可以通过读取primary的方式读取最新成功提交的数据。

在primary-secondary协议中,primary负责进行更新同步操作。在primary-secondary协议中引入Quorum机制,primary成功更新W个节点(含本身)后向用户返回成功。

读取数据时按照一致性要求不同可有不同的做法:

  1. 达到强一致性,直接读取primary中的数据,也可通过增强Quorum机制进行读取最新成功提交数据
  2. 达到会话一致性,根据已读版本号在副本上进行选择性读取(选择版本号 > 已读版本号)
  3. 达到弱一致性,选择任意版本读取

当primary节点异常时,需要选择一个新的节点作为primary,然后secondary与primary进行同步。选择新的primary是由一个中心节点完成的,此时引入Quorum机制,新primary的选择方式类似于读取数据的方式,中心节点读取R个节点副本,选择R个节点副本中版本号最高的作为新的primary(R个节点副本中一定存在最新成功提交的数据)。新primary与至少W个副本完成数据同步后作为新的primary提供读写服务。

需要进一步说明的是,读取R个节点副本能够获取到当前最新的数据,但并不一定是一个成功提交的数据。这时候会出现两种情况(假设系统版本情况为( v 2 , v 2 , v 1 , v 1 , v 1 ),W=3,R=3):
1. R个节点全部读到成功提交的数据( v 1 , v 1 , v 1 ),此时产生的 v 2 为脏数据,可以有多种处理方式解决脏数据,如简单粗暴的删除。
2. R个节点读到的最新数据不是成功提交的数据( v 2 , v 1 , v 1 ),此时新primary副本的最新数据为 v 2 ,虽然 v 2 不是一个成功提交的数据,但是在随后primary与secondary的数据同步后,使 v 2 版本数据达到W个,便能够使得该版本数据成为一个最新成功提交的数据。

那如果使用上一节介绍的增强Quorum机制又是一种什么样的情况呢?留给你自己去思考。^_^

参考

  • 《分布式系统原理介绍》

猜你喜欢

转载自blog.csdn.net/tb3039450/article/details/80249664