Chapter 15 - 复制
Created by : Mr Dk.
2020 / 06 / 09 16:28
Nanjing, Jiangsu, China
Redis 可以通过执行 SLAVEOF
命令或设置选项,去复制另一个服务器。被复制的服务器称为 主服务器 (master),对主服务器进行复制的服务器称为 从服务器 (slave)。
Old Implementation
在 Redis 2.8 之前,使用的是一种旧版本的复制思想,主要分为两步:
- 同步 (sync) - 将从服务器的数据库状态更新到主服务器的状态
- 命令传播 (command propagate) - 在主服务器上运行的命令也要在从服务器上运行一遍
旧版复制方案的问题:本质上相当于 RDB 与 AOF 的差异,可以理解为整体的同步和增量式的同步。整体同步必然会带来较大的开销,新版 Redis 的增量式复制可以有效减小开销。
同步
从服务器向主服务器发送 SYNC
命令,主服务器执行 BGSAVE
命令,在后台生成一个 RDB 文件并发送给从服务器,同时在一个缓冲区中记录从现在开始主服务器执行的所有写命令。从服务器载入 RDB 文件后,执行主服务器中缓存的所有写命令,将数据库状态更新为与主服务器一致。
- RDB 写出与载入
- 写命令缓冲区
命令传播
每当 主服务器 执行客户端发送的写命令时,主服务器会将命令发送给从服务器执行,以保证两个 Redis 服务器的数据一致。
旧版复制的缺陷
从服务器对主服务器的复制分为两种情况:
- 初次复制 - 从服务器从未复制过主服务器,或要复制的主服务器与上次复制的主服务器不同
- 断线后重复制 - 主从服务器在命令传播阶段因网络原因中断复制,但之后服务器自动重连
对于后一种情况,旧版复制显得很累赘,效率很低。为了让服务器补足一小部分缺失的数据,而让主从服务器重新执行一次 SYNC
命令,开销过大。
SYNC
过程中,主服务器生成 RDB 文件需要消耗大量的 CPU、内存、I/O 资源- RDB 文件在网络上的传输会占据较大带宽、流量
- 从服务器在载入 RDB 文件期间会因阻塞无法响应请求
New Implementation
Redis 2.8 开始使用 PSYNC
代替 SYNC
执行同步操作。该命令具有 完整重同步 和 部分重同步 两种模式:
- 完整重同步与原
SYNC
命令基本一致,通过 RDB + 写命令缓冲区进行同步 - 部分重同步处理断线后重复制的情况,只执行连接断开期间主服务器上的写命令
部分重同步的实现
Redis 的主服务器维护了一个 复制积压缓冲区,是一个默认大小为 1MB 的 RingBuffer。主服务器进行命令传播时,不仅会将写命令发送给所有从服务器,还会写入复制积压缓冲区中,从而保存最近 1MB 的写命令。另外,复制积压缓冲区中的每个字节都有其 复制偏移量。
当从服务器连上主服务器时,会将自己的复制偏移量 offset
发送给主服务器。如果 offset
落后于主服务器,且 offset + 1
开始的数据存在于复制积压缓冲区中,则主服务器直接对从服务器进行部分重同步操作;如果 offset + 1
开始的数据已经不存在于复制积压缓冲区中,则主服务器对从服务器执行完整重同步。
复制积压缓冲区的大小可以自行设置。最小大小 = 主服务器每秒写入命令数据量 _ 从服务器重连主服务器的平均时间。为安全起见,可以将最小大小 _ 2。
服务器运行 ID
Redis 服务器在启动时自行生成一个长度为 40 的十六进制字符串。当从服务器第一次复制主服务器时,会保存主服务器的运行 ID。当断线重连后,从服务器向当前主服务器发送运行 ID。如果主服务器发现收到的 ID 和自身 ID 相同,说明这是一次断线重连,可以尝试部分重同步;否则,说明从服务器之前复制的主服务器不是自己,需要进行完整重同步。
心跳检测
从服务器默认以每秒一次的频率,向主服务器发送命令,其中带有从服务器目前的 复制偏移量。命令有三个作用:
- 检测主从服务器的网络连接状态 - 命令是否收到
- 辅助实现 min-slaves 选项 (从服务器数量较少/延迟较高时,主服务器禁止执行写命令)
- 检测命令丢失 - 根据复制偏移量,确认命令传播是否有不成功的情况