基本副本协议

本节讨论基本的副本控制协议,着重分析两大类典型的副本控制协议。

副本控制协议指按特定的协议流程控制副本数据的读写行为,使得副本满足一定的可用性和一致性要求的分布式协议。

副本控制协议要具有一定的对抗异常状态的容错能力,从而使得系统具有一定的可用性,同时副本控制协议要能提供一定一致性级别。

由 CAP 原理可知,要设计一种满足强一致性,且在出现任何网络异常时都可用的副本协议是不可能的。

为此,实际中的副本控制协议总是在可用性、一致性与性能等各要素之间按照具体需求折中。

本文将副本控制协议分为两大类:“中心化副本控制协议”和“去中心化副本控制协议”。

中心化副本控制协议

中心化副本控制协议的基本思路是由一个中心节点协调副本数据的更新、维护副本之间的一致性

中心化副本控制协议的优点是协议相对较为简单,所有的副本相关的控制交由中心节点完成。

并发控制将由中心节点完成,从而使得一个分布式并发控制问题,简化为一个单机并发控制问题。

所谓并发控制,即多个节点同时需要修改副本数据时,需要 解决“写写”、“读写”等并发冲突。

单机系统上常用加锁等方式进行并发控制。

对于分布式并发控制,加锁也是一个常用的方法,但如果没有中心节点统一进行锁管理,就需要完全分布式化的锁系统,会使得协议非常复杂。

中心化副本控制协议的缺点是系统的可用性依赖于中心化节点,当中心节点异常或与中心节点通信中断时,系统将失去某些服务(通常至少失去更新服务),所以中心 化副本控制协议的缺点正是存在一定的停服务时间。

primary-secondary 协议

本文着重介绍一种常用的 primary secondary (也称 primary backup )的中心化副本控制协议。

在 primary secondary 类型的协议中,副本被分为两大类,其中有且仅有一个副本作为 primary 副本,除 primary 以外的副本都作为 secondary 副本。

维护 primary 副本的节点作为中心节点,中心节点负责维护数据的更新、并发控制、协调副本的一致性。

Primary secondary 类型的协议一般要解决四大类问题:数据更新流程、数据读取方式、 Primary副本的确定和切换、数据同步( reconcile )。

以下依次分析。

数据更新基本流程

  1. 数据更新都由 primary 节点协调完成。

  2. 外部节点将更新操作发给 primary 节点

  3. primary 节点进行并发控制即确定并发更新操作的先后顺序

  4. primary 节点将更新操作发送给 secondary 节点

  5. primary 根据 secondary 节点的完成情 况决定更新是否成功并将结果返回外部节点

性能问题

在工程实践中,如果由 primary 直接同时发送给其他 N 个副本发送数据,则每个secondary 的更新吞吐受限于 primary 总的出口网络带宽,最大为 primary 网络出口带宽的 1/N 。

为了解决这个问题,有些系统(例如, GFS ),使用接力的方式同步数据,即 primary 将更新发送给第一个 secondary 副本,第一个 secondary 副本发送给第二 secondary 副本,依次类推。

在提供最终一致性服务的系统中, secondary 节点可以与 primary 不一致,只要后续 secondary 节点可以慢慢同步到与 primary 一致的状态即可满足最终一致性的要求。

数据读取方式

如果只需要最终一致性,则读取任何副本都可以满足需求。

如果需要会话一致性,则可以为副本设置版本号,每次更新后递增版本号,用户读取副本时验证版本号,从而保证用户读到的数据在会话范围内单调递增。

使用 primary secondary 比较困难的是实现强一致性。

这里简单讨论primary secondary 实现强一致性的几种思路。

第一、由于数据的更新流程都是由primary 控制的, primary 副本上的数据一定是最新的,所以如果始终只读 primary 副本的数据,可以实现强一致性。如果只读 p rimary 副本,则 secondary 副本将不提供读服务。

实践中,如果副本不与机器绑定,而是按照数据段为单位维护副本,仅有 primary 副本提供读服务在很多场景下并不会造出机器资源浪费。

将数据分为数据段,以数据段为副本的基本单位,将副本分散到集群中个,假设 primary 也是随机的确定的,那么每台机器上都有 一些数据的 primary 副本,也有另一些数据段的 secondary 副本。

从而某台服务器实际都提供读写服务。

第二、由primary 控制节点 secondary 节点的可用性。

当 primary 更新某个 secondary 副本不成功时, primary 将该 secondary 副本标记为不可用,从而用户不再读取该不可用的副本。

不可用的secondary 副本可以继续尝试与 primary 同步数据,当与 primary 完成数据同步后, primary 可以副本标记为可用。

这种方式使得所有的可用的副本,无论是 primary 还是 secondary 都是可读的,且在一个确定的时间内,某 secondary 副本要 么更新到与 primary 一致的最新状态,要么被标记为不可用,从而符合较高的一致性要求。

这种方式依赖于一个中心元数据管理系统,用于记录哪些副本可用,哪些副本不可用。

某种意义上,该方式通过降低系统的可用性来提高系统的一致性。

第三、基于 Quorum 机制

primary副本的确定与切换

在 primary secondary 类型的协议中,另一个核心的问题是如何确定 primary 副本,尤其是在原 primary 副本所在机器出现宕机等异常时,需要有某种机制切换 primary 副本,使得某个 secondary 副本成为新的 primary 副本。

通常的,在 primary secondary 类型的 分布式 系统中,哪个副本是 primary 这一信息都属于元信息,由专门的元数据服务器维护。

执行更新操作时,首先查询元数据服务器获取副本的 primary 信息,从而进一步执行数据更新流程。

切换副本的难点在于两个方面,

(1)首先,如何确定节点的状态以发现原primary 节点异常是一个较为复杂的问题。

(2)再者,切换 primary后,不能影响副本的一致性。

尤其是提供较强一致性服务的系统,切换 primary 的影响更是需要控制。

要达到这个目的,一种直观的方式是切换的 新 primary 的副本数据必须与原 primary 的副本一致。

然而在原 primary 已经发送宕机等异常时,如何确定一个 secondary 副本使得该副本上的数据与原primary 一致又成为新的问题。

该问题和上节中选择一个 secondary 副本上读取最新的数据是一个等价问题。

上节中本文在 2.4.5 介绍一种基于 Quorum 机制确定新 primary 的方法。

由于分布式系统中可靠的发现节点异常是需要一定的探测时间的,这样的探测时间通常是10秒级别(见 2.3.3 使用 lease 确定节点状态),这也意味着一旦 primary 异常,最多需要 10 秒级别的发现时间,系统才能开始 primary 的切换,在这 10 秒时间内,由于没有 primary,系统不能提供更新服 务,如果系统只能读 primary 副本,则这段时间内甚至不能提供读服务。

从这里可以看到,primary backup 类副本协议的最大缺点就是由于 primary 切换带来的一定的停服务时间。

数据同步

Primary secondary 型协议一般都会遇到 secondary 副本与 primary 不一致的问题。

此时,不一致的 secondary 副本需要与 primary 进行同步( reconcile )。

通常不一致的形式有三种:

一、由于网络分化等异常,secondary 上的数据落后于 primary 上的数据。

二、在某些协议下, secondary 上的数据有可能是脏数据,需要被丢弃。所谓脏数据是由于primary 副本没有进行某一更新操作,而 secondary 副本上反而进行的多余的修改操作,从而造成secondary 副本数据错误。

三、 secondary 是一个新增加的副本,完全没有数据,需要从其他副本上拷贝数据。

去中心化副本控制协议

去中心化副本控制是另一类较为复杂的副本控制协议。

与中心化副本系统协议最大的不同是,去中心化副本控制协议没有中心节点,协议中所有的节点都是完全对等的,节点之间通过平等协商达到一致。

从而去中心化协议没有因为中心化节点异常而带来的停服 务等问题。

然而,没有什么事情是完美的,去中心化协议的最大的缺点是协议过程通常比较复杂。

尤其当去中心化协议需要实现强一致性时,协议流程变得复杂且不容易理解。

由于流程的复杂,去中心化协议的效率或者性能一般也较中心化协议低。(专治的性能高于公平)

Paxos 是唯一在工程中得到应用的强一致性去中心化副本控制协议。

参考资料

《分布式系统原理》