跳到主要内容

主备复制

1. 什么是主备复制

1.1. Primary-Backup 架构的挑战

对于 Primary-Backup 架构的分布式系统,一个最重要的挑战,就是如何实现主节点与备节点之间的一致性。将主节点的状态同步到备节点的过程,就是主备复制。

采用 Primary-Backup 架构的分布式系统有很多,比如 MySQL、Redis、ZooKeeper 等,它们实现主备同步的方式也不尽相同。但是抽象其同步方式,最终都可以归纳为以下两种:

  1. 状态转移(State Transfer)
  2. 复制状态机(Replicated State Machine,RSM)

1.2. 分布式系统的"状态"

在正式介绍这两种复制方式之前,我们先谈谈分布式系统的"状态"。

对于一个系统来说,它的状态就是描述当前系统运行情况的数据集合。这里更侧重的是"数据",举一个简单的例子:

一个 KV 存储系统,比如 Redis,它的状态就是所有已存储的键值对。如果我们想要增加一个 Redis 的备节点,那么就需要将主节点所有的数据都复制给备节点。

2. 状态转移(State Transfer)

2.1. 什么是状态转移

状态转移(State Transfer),顾名思义,就是将主节点的状态完整地**转移(复制)**给备节点。

这也是很容易理解的一种复制方式,不论主节点有什么内容,对于备节点来说,原封不动地 Copy 一份,那么就得到了一个与主节点完全一致的副本。

状态转移

2.2. 状态转移的优势

由于只需要同步主节点的全量状态,这就是状态转移最大的优势——实现简单

相比于其他需要记录日志、权衡各种分布式算法的同步方式,状态转移只需要将主节点的状态完整地复制一份给备节点即可。

当然简单往往也伴随着挑战。虽然实现很简单,但是在真实环境中,也有很多问题需要面对。

2.3. 状态转移的问题

1. 难以收集全部的状态

对于大部分系统来说,我们只需要关注存储系统中的数据即可,将这些数据复制一份,那么就有了一个与主节点完全一样的副本。但是对于一些特殊的系统来说,涉及到的状态数量多,可能分布在各个地方,内存、硬盘等等,收集所有的数据并不是一件容易的事情。

对于分布式系统的开发者来说,这也是要在系统设计时就需要考虑的问题,使用状态转移的方式进行主备复制,如何定义状态的全集。

2. 增量状态无法同步

第二个问题是,在备节点同步主节点的状态时,如果主节点有更新,正在同步的副本中并不会包含这些增量数据,导致即使同步之后,也会面临不一致的问题。

一种解决方案是在同步期间停止主节点的写入,当然代价就是主节点的可用性降低。

3. 全量同步的性能损耗

最后一个问题,对于一个已经运行的主节点来说,可能已经积累了大量的数据,如果把这些状态数据全部复制一份,这个过程需要的时间成本会很高,尤其面对跨机房、跨地域的场景,这个过程会更加漫长。

3. 复制状态机(Replicated State Machine, RSM)

3.1. 什么是复制状态机

如果每次同步的数据少一些,是否可以解决状态转移面对的全量同步问题?

答案是肯定的,这就是复制状态机(Replicated State Machine, RSM)做的事情。

简单来说,复制状态机(RSM)就是将主节点上的写入操作,在备节点上按照相同的顺序执行一遍,那么经过与主节点完全一致的变化过程后,二者的状态自然也就一致了。

在复制状态机的方案下,主节点需要记录所有的写入操作,并且将这些操作同步给备节点。

复制状态机

3.2. 复制状态机的优势

复制状态机解决了状态转移面对的几个问题:

  1. 相比于去多个地方收集状态,复制状态机只需要记录写入操作,操作的粒度更小,更容易收集
  2. 主节点有增量更新时,只需要将增量的操作日志同步给备节点,避免了全量的同步损耗
  3. 全量同步时,可以分批次将操作日志同步给备节点,避免了单次同步数据量过大的问题

3.3. 复制状态机的劣势

同样,复制状态机也有一些问题需要面对:

1. 全量同步时间可能会很长

如果主节点已经运行了很长时间,那么写入操作的日志规模同样会很大,甚至可能会远超全量副本的大小。那么状态转移所需要面对的网络延时、同步规模等问题,在复制状态机方案下同样存在。

2. 实现复杂度

相比于状态转移,复制状态机需要记录所有的写入操作,并且将这些操作同步给备节点。这就需要一个共识协议来保证所有备节点执行相同的操作顺序。这也会引出另一个话题,也是当下最主流的共识协议:Raft。

4. 状态转移与复制状态机结合

既然二者都有各自的优劣势,那么是否可以结合二者,取长补短呢?

答案是肯定的,这就是当下最主流的复制方式:半同步复制

我们将同步的过程分为两个阶段:

  1. 初始化阶段。此时备节点与主节点的状态相差很多,此时使用全量同步,可以最快地将备节点的状态与主节点保持尽可能的一致
  2. 增量阶段。同步了尽可能多的主节点数据后,难免还有一些增量数据没有同步。此时主备之间在同步增量数据的操作日志,备节点在本地执行这些操作,逐渐追上主节点的进度,最终达到一致

如果大家了解 Redis 的同步,就会发现在 Redis 中,两种复制方式都有使用,而且 Redis Sentinel 模式下,增加新的节点时,也会结合这两种同步方式。

5. 状态转移 vs 复制状态机:对比总结

维度状态转移(State Transfer)复制状态机(RSM)
核心方式主节点传输完整状态所有节点执行相同操作
一致性弱/最终一致性强一致性
网络开销高(大状态)相对低(操作较小)
实现复杂度高(需共识协议)
应用场景Redis、MySQL 异步复制等etcd、ZooKeeper 等

6. 总结

对于 Primary-Backup 架构的分布式系统,主备复制是保证数据一致性的重要手段。所有的复制最终又可以归纳为两种方式:状态转移和复制状态机。

状态转移的实现,要求主节点将当下的状态全部复制到备节点中,这种方式实现简单,但是如果当前状态数据过多,那么同步时,网络开销会非常大。

复制状态机的实现,要求主节点将所有的写入操作记录下来,并且将这些操作同步给备节点,备节点在本地执行这些操作,最终达到与主节点一致的状态。这种方式实现复杂,但是网络开销相对较小。

在实际环境中,更好的方式是结合这两种方式,在初始化阶段使用状态转移,在增量阶段使用复制状态机,这样既减小了网络开销,同时也保证了增量数据可以被正确同步。

7. 面试建议

实际面试中,很少会专门地提问主备复制的方式,更多地是结合实际的中间件,考察某一个中间件的实现方式,比如 Redis 的 Sentinel 模式,或者 MySQL 的复制方式等。也可能会出现在系统设计的题目中,考察如何实现某个组件的主备复制等。