分布式系统原理
基本副本协议
副本控制协议指按特定的协议流程控制副本数据的读写行为,使得副本满足一定的可用性和一致性要求的分布式协议。
副本控制协议要具有一定的对抗异常状态的容错能力,从而使得系统具有一定的可用性,同时副本控制协议要能提供一定一致性级别。
本文将副本控制协议分为两大类:“中心化(centralized)副本控制协议”和“去中心化(decentralized)副本控制协议”。
1. 中心化副本控制协议
中心化副本控制协议的基本思路是由一个中心节点协调副本数据的更新、维护副本之间的一致性。
中心化副本控制协议的优点是协议相对较为简单,所有的副本相关的控制交由中心节点完成。并发控制将由中心节点完成,从而使得一个分布式并发控制问题,简化为一个单机并发控制问题。
中心化副本控制协议的缺点是系统的可用性依赖于中心化节点,当中心节点异常或与中心节点通信中断时,系统将失去某些服务(通常至少失去更新服务),所以中心化副本控制协议的缺点正是存在一定的停服务时间。
2. primary-secondary 协议
本文着重介绍一种非常常用的 primary-secondary (也称 primary-backup)的中心化副本控制协议。在 primary-secondary类型的协议中,副本被分为两大类,其中有且仅有一个副本作为 primary 副本,除 primary 以外的副本都作为 secondary 副本。维护 primary 副本的节点作为中心节点,中心节点负责维护数据的更新、并发控制、协调副本的一致性。
Primary-secondary类型的协议一般要解决四大类问题:*数据更新流程、数据读取方式、Primary
副本的确定和切换、数据同步(reconcile)*。
2.1 数据更新基本流程
流程 2.2.1: Primary-secondary 协议的数据更新流程如下:
1. 数据更新都由 primary 节点协调完成。
2. 外部节点将更新操作发给 primary 节点
3. primary 节点进行并发控制即确定并发更新操作的先后顺序
4. primary 节点将更新操作发送给 secondary 节点
5. primary 根据 secondary 节点的完成情况决定更新是否成功并将结果返回外部节点
- 由于异常,第 4 步可能在有些副本上成功,有些副本上失败,在有些副本上超时。不同的副本控制协议对于第 4 步异常的处理都不一样。例如,在提供最终一致性服务的系统中,secondary 节点可以与 primary 不一致,只要后续 secondary 节点可以慢慢同步到与 primary 一致的状态即可满足最终一致性的要求。对于第 4 步的具体处理,本节先不展开讨论,在 2.4 中介绍一种基于 Quorum 的副本控制机制。
2.2 数据读取方式
与数据更新流程类似,读取方式也与一致性高度相关。
如果只需要最终一致性,则读取任何副本都可以满足需求。
如果需要会话一致性,则可以为副本设置版本号,每次更新后递增版本号,用户读取副本时验证版本号,从而保证用户读到的数据在会话范围内单调递增。
使用 primary-secondary 比较困难的是实现强一致性。
这里简单讨论 primary-secondary 实现强一致性的几种思路
第一、由于数据的更新流程都是由 primary 控制的,primary 副本上的数据一定是最新的,所以如果始终只读 primary 副本的数据,可以实现强一致性。如果只读 primary 副本,则 secondary 副本
将不提供读服务。实践中,如果副本不与机器绑定,而是按照数据段为单位维护副本,仅有 primary
副本提供读服务在很多场景下并不会造出机器资源浪费。第二、由 primary 控制节点 secondary 节点的可用性。当 primary 更新某个 secondary 副本不成功时,primary 将该 secondary 副本标记为不可用,从而用户不再读取该不可用的副本。这种方式依赖于一个中心元数据管理系统,用于记录哪些副本可用,哪些副本不可用。该方式通过降低系统的可用性来提高系统的一致性
第三、基于 Quorum 机制。
2.3 primary 副本的确定与切换
在 primary-secondary 类型的协议中,另一个核心的问题是如何确定 primary 副本,尤其是在原primary 副本所在机器出现宕机等异常时,需要有某种机制切换 primary 副本,使得某个 secondary副本成为新的 primary 副本。
通常的,在 primary-secondary 类型的分布式系统中,哪个副本是 primary 这一信息都属于元信息,由专门的元数据服务器维护。执行更新操作时,首先查询元数据服务器获取副本 primary 信息,从而进一步执行数据更新流程。
切换副本的难点在于两个方面,首先,如何确定节点的状态以发现原 primary 节点异常是一个较为复杂的问题。在 2.3 中,详细介绍一种基于 Lease 机制确定节点状态的方法。再者,切换 primary后,不能影响副本的一致性。本文在 2.4.5 介绍一种基于 Quorum 机制确定新 primary 的方法。
2.4 数据同步
Primary-secondary 型协议一般都会遇到 secondary 副本与 primary 不一致的问题。此时,不一致的 secondary 副本需要与 primary 进行同步(reconcile)。通常不一致的形式有三种:
一、由于网络分化等异常,secondary 上的数据落后于 primary 上的数据。对于第一种 secondary 数据落后的情况,常见的同步方式是回放 primary 上的操作日志(通常是redo 日志),从而追上 primary 的更新进度。
二、在某些协议下,secondary 上的数据有可能是脏数据,需要被丢弃。所谓脏数据是由于primary 副本没有进行某一更新操作,而 secondary 副本上反而进行的多余的修改操作,从而造成secondary 副本数据错误。对于脏数据的情况,较好的做法是设计的分布式协议不产生脏数据。
三、secondary 是一个新增加的副本,完全没有数据,需要从其他副本上拷贝数据。如果 secondary 副本完全没有数据,则常见的做法是直接拷贝 primary 副本的数据,这种方法往往比回放日志追更新进度的方法快很多。但拷贝数据时 primary 副本需要能够继续提供更新服务,这就要求 primary 副本支持快照(snapshot)功能。即对某一刻的副本数据形成快照,然后拷贝快照,拷贝完成后使用回放日志的方式追快照形成后的更新操作。
3. 去中心化副本控制协议
去中心化副本控制是另一类较为复杂的副本控制协议。与中心化副本系统协议最大的不同是,去中心化副本控制协议没有中心节点,协议中所有的节点都是完全对等的,节点之间通过平等协商达到一致。从而去中心化协议没有因为中心化节点异常而带来的停服务等问题。
去中心化协议的最大的缺点是协议过程通常比较复杂。尤其当去中心化协议需要实现强一致性时,协议流程变得复杂且不容易理解。由于流程的复杂,去中心化协议的效率或者性能一般也较中心化协议低。
Paxos 是唯一在工程中得到应用的强一致性去中心化副本控制协议。
4. 工程投影
GFS 中的 Primary-Secondary 协议。GFS 系统的副本控制协议是典型的 Primary-Secondary 型协议,Primary 副本由 Master 指定,Primary 副本决定并发更新操作的顺序。
PNUTS 中的 Primary-Secondary 协议。PNUTS 的副本控制协议也是典型的 primary-secondary 型协议。Primary 副本负责将更新操作向YMB 中提交,当更新记录写入 YMB 则认为更新成功。YMB 本身是一个分布式的消息发布、订阅系统,其具有多副本、高可用、跨地域等特性。
Niobe 中的 Primary-Secondary 协议。Niobe 协议又是一个典型的 primary-secondary 型协议。Niobe 协议中,primary 信息由 GSM 模块维护,更新操作由 primary 副本同步到 secondary 副本。
Dynamo/Cassandra 的去中心化副本控制协议。Dynamo / Cassandra 使用基于一致性哈希的去中心化协议。虽然 Dynamo 尝试通过引入 Quorum机制和 vector clock 机制解决读取数据的一致性问题,但其一致性模型依旧是一个较大的问题。
Chubby/Zookeeper 的副本控制协议。Chubby[13]和 Zookeeper 使用了基于 Paxos 的去中心化协议选出 primary 节点,但完成 primary节点的选举后,这两个系统都转为中心化的副本控制协议,即由 primary 节点负责同步更新操作到secondary 节点。
Megastore 的副本控制协议。虽然都使用了 Paxos 协议,但与 Chubby 和 Zookeeper 不同的是,Megastore 中每次数据更新操作都基于一个改进的 Paxos 协议的实例,而不是利用 paxos 协议先选出 primary 后,再转为中心化的primary-secondary 方式。
其他系统的副本控制协议。Mola*/Armor*和 Big Pipe*也无一例外的使用了 Primary-secondary 协议控制副本。
参考:《分布式系统原理介绍》 - 刘杰