多主复制

在主从复制的场景中,只有一个主节点,所有的写入操作都要先经过主节点,主节点压力大的问题还是没能解决。而且单主节点的容灾效果也不是很好。

为了达到好的容灾效果,各个机房的距离应该足够远,尽可能的分布在不同的地区,那么这种场景下,用户直接读写自己最近的数据中心,网络延迟最小,效果最好。因此就出现了多住复制。

如何实现

它是指在一个数据系统中,存在多个主从 复制单元,每一个主从复制单元都可以处理读写请求,一个主从复制单元的主副本处理了写请 求后,需要复制到其他的主从复制单元的主副本,具体的流程见下图:

image-20231004112141400

有几点需要注意:

首先,每一个主从复制单元内部是一个常规的主 从复制模式,这里的主副本、从副本之间的复制可以是同步的,也可以是异步的。

其次,多个主从复制单元之间,每一个主副本都会将自己的修改复制到其他的主副本,主副本 之间的复制可以是同步的,也可以是异步的。

如果主副本之间的复制是同步的,那么一个主副本的写入,需要等待复制到其他的主副本成功 后,才能返回给用户,但是,这样却失去了多主复制最重要的一个优点,即多个主副本都可以独立处理写入,这就导致整个模式 退化为主从复制的形式。所以一般来说,多主复制的主副本之间,大多采用异步模式。

但采用异步复制也会出现问题,如果多个主副本同时成功修 改一个数据,当主副本之间复制这个数据的修改时,会出现冲突,我们就不知道以哪一个主副 本的写入结果为准了,该问题在同步复制时可以让用户决定哪个为主。

冲突解决

冲突主要由两种形式,

首先是由于更新导致的冲突,多个主副本同时更新了一个数据,导致这个数据的版本是非线性的,出现了分叉,具体见下图:

image-20231004124557885其次,由于新增导致的冲突,多个主副本同时新增了一个含有唯一性约束的数据,导致数据的唯一性约束被破坏。例如,在酒店预订业务中,一个时段内一个房间只能预订给一个用户,如 果多个用户在多个主副本上,同时发起预订操作,就可能出现同一个时段内,一个房间被多个 用户预定成功的情况。

避免冲突

由上可知,冲突是多个主副本同时 修改了一个数据,或者破坏了数据的唯一性约束导致的,那么我们就对数据进行分片,让不同的主数据负责不同的数据分片,这个方式可以在一定程度上避免冲突,但是会导致两个问题:

首先,一个修改操作可能会修改多个分片数据,这样我们就没有办法通过分片来隔离修改了。

其次,由于就近接入和故障等原因,我们会将出现故障的主副本流量切换到其他的主副本,这 时也会出现写入冲突的情况。

注意:这里的分片,并不是每个主副本只保留一部分数据,而是每个主副本仍然保留全量的数据,但是只负责主动的修改某一部分,其他部分等待其他主副本进行同步。

写时解决冲突

写时解决冲突有两种实现,预定义解决冲突和自定义解决冲突。

预定义解决冲突,是指由存储系统预先定义好规则,在冲突发生时依据预先定义好的规则,自动来解决冲突,主要有以下几种:

  1. 从操作维度来处理,最后写入获胜。也就是为每一个写操作分配一个时间戳,如果发生 冲突,只保留时间戳最大的版本数据,其他的修改都丢弃,但是这个方法会导致修改丢失。
  2. 从副本维度来处理,最高优先级写入获胜。也就是为每一个副本都排好优先级,如果发 生冲突,只保留优先级最高的副本修改数据,其他的修改都丢弃。
  3. 从数据结构和算法的维度来处理,通过研究一些可以自动解决冲突的数据结构来解决问题。目前不成熟。

自定义解决冲突,它是由业务系统来定义冲突的解决方式,如果发生冲突 了,存储系统就依据业务系统定义的方式执行。

自定义冲突解决的处理逻辑是,在主副本之间复制变更日志时,如果检测到冲突,就调用用户 自定义的冲突处理程序来进行处理。由于主副本之间的数据复制是异步的,所以一般都是后台 执行,不会提示用户。

读时解决冲突

读时解决冲突的思路和写时解决冲突的思路正好相反,即在写入数据时,如果检测到冲突,不 用立即进行处理,只需要将所有冲突的写入版本都记录下来。当下一次读取数据时,会将所有的数据版本都返回给业务层,在业务层解决冲突,那么读时解决冲突的方式有下面两种:

  1. 由用户来解决冲突。业务层将冲突提示给用户,让用户来解决。
  2. 自定义解决冲突。业务层先依据业务情况,自定义好解决冲突的处理程序,当检 测到冲突时,直接调用处理程序来解决。

多主复制的关键问题

1、正确解决冲突的难度非常大。

2、异步模式的多主复制会存在数据一致性的问题。因为多个主副本都是独立写入的,而他们之间是通过异步复制的方式。

3、多个主副本之间的复制拓扑结构问题。一般来说,多主复制的主副本之间的复制拓扑结 构主要有三种:环形拓扑、星形拓扑以及全部至全部拓扑,具体见下图:

image-20231004155403956

前两种,如果一个主副本出现问题,则会导致整个副本的数据无法同步,而第三种虽然一个挂了不影响,但是他们主副本之间同步的时延却要大很多。

参考

《深入浅出分布式技术原理》