5. 复制
与可能出错的东西比,’不可能’出错的东西最显著的特点就是:一旦真的出错,通常就彻底玩完了。
——道格拉斯·亚当斯(1992)
复制意味着在通过网络连接的多台机器上保留相同数据的副本。正如在第二部分简介中所讨论的那样,我们希望能复制数据,可能出于各种各样的原因:
- 使得数据与用户在地理上接近(从而减少延迟)
- 即使系统的一部分出现故障,系统也能继续工作(从而提高可用性)
- 扩展可以接受读请求的机器数量(从而提高读取吞吐量)
本章将假设你的数据集非常小,每台机器都可以保存整个数据集的副本。在第6章中将放宽这个假设,讨论对单个机器来说太大的数据集的分割(分片)。在后面的章节中,我们将讨论复制数据系统中可能发生的各种故障,以及如何处理这些故障。
如果复制中的数据不会随时间而改变,那复制就很简单:将数据复制到每个节点一次就万事大吉。复制的困难之处在于处理复制数据的变更(change),这就是本章所要讲的。我们将讨论三种流行的变更复制算法:单领导者(single leader),多领导者(multi leader)和无领导者(leaderless)。几乎所有分布式数据库都使用这三种方法之一。
在复制时需要进行许多权衡:例如,使用同步复制还是异步复制?如何处理失败的副本?这些通常是数据库中的配置选项,细节因数据库而异,但原理在许多不同的实现中都类似。本章会讨论这些决策的后果。
数据库的复制算得上是老生常谈了 ——70年代研究得出的基本原则至今没有太大变化【1】,因为网络的基本约束仍保持不变。然而在研究之外,许多开发人员仍然假设一个数据库只有一个节点。分布式数据库变为主流只是最近发生的事。许多程序员都是这一领域的新手,因此对于诸如最终一致性(eventual consistency)等问题存在许多误解。在“复制延迟问题”一节,我们将更加精确地了解最终的一致性,并讨论诸如读己之写(read-your-writes)和单调读(monotonic read)保证等内容。