🗒️主从库数据同步
2024-9-19
| 2024-9-19
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password

主从库模式

主从库模式,主从库之间采用读写分离的方法:
读操作:主库、从库都可以接受
写操作:首先到主库执行,然后,主库将写操作同步到从库
notion image

为什么要采用读写分离的方式呢?

如果不采用读写分离的方式,数据需要多个 Redis 之间同步,可能涉及到加锁等操作,且 Redis 之间是基于网络通信的,每个 Redis 都需要将自己的数据同步到其他实例上,可能会造成网络信息的拥堵。
而采用读写分离的方法,所有写操作仅仅在主库上进行操作,避免了为保证数据不一致带来的额外开销。其次,仅仅需要将主库的信息同步到从库上,不需要每个实例之间通信,在一定程度上缓解了网络通信的拥堵。

操作命令──replicaof(Redis 5.0 之前使用 slaveof)命令

例如,现在有实例 1(ip:172.16.19.3)和实例 2(ip:172.16.19.5),我们在实例 2 上执行以下这个命令后,实例 2 就变成了实例 1 的从库,并从实例 1 上复制数据

主从库间数据第一次同步的三个阶段

notion image
  1. 主从库间建立连接、协商同步
      • psync 命令──主库的 runID 和复制进度 offset
        • runID,是每个 Redis 实例启动时都会自动生成的一个随机 ID,用来唯一标记这个实 例。当从库和主库第一次复制时,因为不知道主库的 runID,所以将 runID 设 为“?”。
        • offset,此时设为 1,表示第一次复制。
      • FULLRESYNC 命令──主库 runID 和主库目前的复制进度 offset
        • 两个参数的作用同上
        • 注意:FULLRESYNC 响应表示第一次复制采用的全量复制。
  1. 主库将所有数据同步给从库──依赖与 RDB 文件
      • 流程
          1. 主库生成 RDB 文件,将其发送给从库
          1. 从库接受 RDB 文件,清空当前数据库,加载 RDB 文件
      • 注:同步过程中,主库可以正常响应请求。新来的写操作会存放到 replication buffer 中,其中 RDB 文件生成后收到的所有写操作
  1. 主库会把第二阶段执行过程中新收到的写命令,再发送给从库──传输的是命令

主从级联模式──“主-从-从”模式分担全量复制时的主库压力

两个耗时操作:生成 RDB 操作和传输 RDB 文件。
当从库数量众多,且都需要和主库进行全量服务,会给主库的资源带来压力。原因是所有的从库都是和主库连接,所有的全量复制也都是和主库进行的。我们可以通过“主 - 从 - 从”模式将主库生成 RDB 和传输 RDB 的压力,以级联的方式分散到从库上。
notion image

全量复制完成之后──基于长连接的命令传播

一旦主从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,可以避免频繁建立连接的开销长连接复制是主从库正常运行后的常规同步阶段。在这个阶段中,主从库之间通过命令传播实现同步。

主从库网络断联──增量复制

当主从库断连后,主库会把断连期间收到的写操作命令,写入 replication buffer,同时也会把这些操作命令也写入 repl_backlog_buffer 这个缓冲区。
repl_backlog_buffer 是一个环形缓冲区,主库会记录自己写到的位置(master_repl_offset 从库则会记录自己已经读到的位置(slave_repl_offset
notion image
在网络断连阶段,主库可能会收到新的写操作命令,所以,一般来说,master_repl_offset 会大于 slave_repl_offset。主从库的连接恢复之后,从库首先会给主库发送 psync 命令,并把自己当前的 slave_repl_offset 发给主库,主库会判断自己的 master_repl_offsetslave_repl_offset 之间的差距。此时,主库只用把 master_repl_offsetslave_repl_offset 之间的命令操作同步给从库就行。
notion image
注:如果从库的读取速度比较慢,就有可能导致从库还未读取的操作被主库新写的操作覆盖了,这会导致主从库间的数据不一致。──调整repl_backlog_size 参数
缓冲空间的计算公式是:缓冲空间大小 = 主库写入命令速度 * 操作大小 - 主从库间网络传输命令速度 * 操作大小。而 repl_backlog_size = 缓冲空间大小 * 2
举个例子,如果主库每秒写入 2000 个操作,每个操作的大小为 2KB,网络每秒能传输 1000 个操作,那么,有 1000 个操作需要缓冲起来,这就至少需要 2MB 的缓冲空间。否则,新写的命令就会覆盖掉旧操作了。为了应对可能的突发压力,我们最终把 repl_backlog_size 设为 4MB。
不过,如果并发请求量非常大,连两倍的缓冲空间都存不下新操作请求的话,此时,主从库数据仍然可能不一致。针对这种情况,一方面,你可以根据 Redis 所在服务器的内存资源再适当增加 repl_backlog_size 值,比如说设置成缓冲空间大小的 4 倍,另一方面,你可以考虑使用切片集群来分担单个主库的请求压力。

总结

三种模式:
  • 全量复制:主从库直接第一次同步,会采用全量复制。
  • 基于长连接的命令传播:在完成全量复制后,长连接复制是主从库正常运行后的常规同步阶段。在这个阶段中,主从库之间通过命令传播实现同步。──注:传输的是命令,不是数据。
  • 增量复制:在长连接复制阶段,如果遇到了网络断连,增量复制就派上用场了。──注:传输的是命令,不是数据。
注:在主从同步中,除了一开始是用 RDB 文件传输数据,之后都是传输操作命令的。replication bufferrepl_backlog_buffer 都是存储的写操作命令。

问题

为什么主从库间的复制不使用 AOF?
我的理解:AOF 的文件通常比 RDB 文件大,在网络传输中,耗时较大。其次,AOF 文件中存的是命令,还需要通过命令逐行恢复。

📎 参考

  • Redis
  • 哨兵机制并发设计模式总览
    Loading...
    目录