数据复制

一个可能出错的事物和一个不可能出错的事物之间的主要区别是,当一个不可能出错的事物出错了,通常意味着不可修复

​ ---------------Douglas Adams,《基本无害》(1992)

1.复制的目的

  • 高可用性:即使某台机器(或多台机器,或整个数据中心)出现故障,系统也能保持正常运行。
  • 连接断开与容错:允许应用程序在出现网络中断时继续工作
  • 低延迟:将数据放置在距离用户较近的地方,从而实现更快地交互
  • 可扩展性:采用多副本读取,大幅提高系统读操作的吞吐量

2.主从复制

2.1同步复制与异步复制

同步复制即数据从主节点写入,同步到从节点,等从节点也写成功了才返回成功,否则失败。优点:主从节点数据同步,不存在滞后问题。缺点:耗时长,当从节点数据量多的时候,很容易造成阻塞。

异步复制即数据从主节点写入,成功则直接返回,然后采用异步的方式比如mq写入从节点,不等待从节点写入结果。优点:耗时短。缺点:主从节点数据不一致,有可能造成数据丢失,用户读取不同节点可能返回结果不同造成混乱。

半同步复制即一台机器采用同步复制,其余机器采用异步复制的方式。优点:耗时较短,可以增强读性能。缺点:依旧有可能造成数据丢失等问题

2.2复制日志的实现

①基于语句的复制

主节点记录所执行的每个写请求并将该操作语句作为日志发送给从节点。优点:简单直接。缺点:一些函数比如获取时间,有些语句之间有依赖关系,可能会出现不一致。

②基于预写日志传输

所有对数据库写入的字节序列都被记入日志。优点:可以建立完全相同的副本。缺点:和存储引擎紧密耦合

③基于行的逻辑日志复制

将复制与存储逻辑解耦,生成逻辑日志,类似mysql的binlog。能够与存储引擎解耦,并完整的复制数据。

④基于触发器的复制

在应用层进行数据的复制。优点:更加的灵活多变,可以只复制部分数据,解决冲突等。缺点:需要业务自主实现。

2.3复制滞后问题

①写后读一致性

用户在写入数据能立即读到最新的数据。

实现方案:

  • 如果用户访问可能会被修改的内容,从主节点读取;否则,在从节点读取。
  • f防止大部分读取请求都走主节点,丧失读扩展性,跟踪最近更新的时间,如果更新后一分钟之内,则总是在主节点读取。
  • 客户端还可以记住最近更新时的时间戳,并附带在读请求中,据此信息,系统可以确保对该用户提供读服务时都应该至少包含了该时间戳的更新。
  • 如果副本分布在多数据中心,必须先把请求路由到主节点所在的数据中心。
②单调读一致性

如果某个用户依次进行多次读取,则他绝不会看到回滚现象,即在读取较新值之后又发生读旧值的情况。

实现方案:

  • 确保每个用户总是从固定的同一副本读取。
③前缀一致读

对于一系列按照某种顺序发生的写请求,那么读取这些内容时也会按照当时写入的顺序。

实现方案:

  • 确保任何具有因果顺序关系的写入都交给一个分区来完成。

3.多主节点复制

对主从复制模型自然的扩展,则可以配置多个主节点,每个主节点都可以接受写操作,后面复制的流程类似:处理写的每个主节点必须将该数据更改转发到所有其他节点。

3.1适用场景

①多数据中心

在每个数据中心内,采用常规的主从复制方案;而在数据中心之间,由各个数据中心的主节点来负责同其他数据中心的主节点进行数据的交换,更新。

②离线客户端操作

一些终端设备,比如手机,ipad,在网络断开后还需要继续工作,可以单独看作一个数据中心处理,独立的写入数据,在连上网络后需要同步数据。

3.2处理写冲突

①避免冲突

处理冲突最理想的策略是避免发生冲突,即如果应用层可以保证对特定记录的写请求总是通过同一个主节点,这样就不会发生写冲突。

②收敛于一直状态
  • 给每个写入分配唯一的ID。
  • 为每个副本分配一个唯一的ID。
  • 以某种方式将这些值合并在一起。
  • 利用预定义好的格式来记录和保留冲突相关的所有信息,然后依靠应用层的逻辑,事后解决冲突(可能会提示用户)。
③自定义冲突解决逻辑

解决冲突最合适的方式可能还是依靠应用层。

  • 在写入时执行。只要数据库系统在复制变更日志时检测到冲突,就会调用应用层的冲突处理程序。
  • 在读取时执行。将所有冲突都保存下来,在下一次读取时返回给应用层,由应用层解决后反馈给数据库。
④自动冲突解决
  • 无冲突的复制数据类型(CRDT)。
  • 可合并的持久数据结构。
  • 操作转换。

4.无主节点复制

放弃主节点,允许任何副本直接接受来自客户端的写请求。

4.1读写quorum

如果有n个副本,写入需要w个节点确认,读取必须至少查询r个节点,则只要w+r>n,读取的节点中一定包含最新值(通过版本号来区分数据的新旧)。n一般设置为奇数,当n=5,w=3,r=3时,可以容忍两个不可用的节点。通常,读取和写入总是并行发送到所有的n个副本,参数w和参数r只是决定要等待的节点数。即有多少个节点需要返回结果,我们才能判断出结果的正确性。

4.2读修复和反熵过程

当一个节点失效后重新上线,数据的同步主要有两种方法

  • 读修复。读取数据时读到了旧数据则进行更新,适合频繁读取数据的场景
  • 反熵过程。启动后台进程查找各个副本间的差异,进行数据的同步处理。

4.3quorum一致性的局限性

在w+r>n的情况下,也可能存在返回旧值的边界条件。

  • 如果采用了sloppy quorum,写操作的w节点和读取的r节点可能完全不同,因此无法保证读写请求一定存在重叠的节点
  • 如果两个写操作同时发生,则无法明确先后顺序。
  • 如果写操作与读操作同时发生,写操作可能仅在一部分副本上完成
  • 如果某些副本上已经写入成功,而其他一些副本发生写入失败,且总的成功副本数据少于w,那些已成功的副本上不会做回滚。这时写是失败的,但读可能是新值。
  • 如果具有新值的节点后来发生失效,但恢复数据来自某个旧值,打破判断条件。

5.总结

数据的复制能够从整体上提升系统的可用性,降低延迟,提高扩展性,对于不同的系统独立分析,从节点失效,网络不可靠,副本一致性,持久性,可用性和延迟这些角度出发,权衡处理,选择最适合自己系统的复制方式。


willian
9 声望1 粉丝

一枚开始记载自己成长日记的程序员