Redis - 主从数据同步:全量复制、增量复制与级联模式

为什么需要主从库
Redis 的高可靠性包含两层含义:数据尽量少丢失,服务尽量少中断。AOF 和 RDB 解决了前者,而主从库模式解决的是后者——通过增加副本冗余量,将同一份数据保存在多个实例上,即使某个实例故障,其他实例仍可对外提供服务。
主从库之间采用读写分离的方式:
- 读操作:主库、从库都可以接收
- 写操作:只在主库执行,然后同步给从库

为什么不让所有实例都能写?因为如果客户端对同一个 key 的多次修改分别发到不同实例,各实例上的数据就会不一致。要保持一致就需要加锁和协商,开销巨大。读写分离让所有修改都在主库进行,主库再同步给从库,简单高效。
第一次全量同步
当从库执行 replicaof 命令与主库建立主从关系后,会经历三个阶段完成首次数据同步:
replicaof 172.16.19.3 6379

第一阶段:建立连接,协商同步
从库给主库发送 psync 命令,携带两个参数:
runID:主库的运行 ID。首次复制时不知道主库 runID,设为 “?”offset:复制进度偏移量,首次设为 -1
主库收到后,用 FULLRESYNC 命令响应,返回自己的 runID 和当前的复制进度 offset。FULLRESYNC 表示采用全量复制。
第二阶段:主库同步数据给从库
主库执行 bgsave 生成 RDB 文件,发送给从库。从库收到后先清空当前数据库,再加载 RDB 文件。
这个过程中主库不会阻塞,仍然正常接收请求。但新收到的写操作不在 RDB 文件中,主库会把这些操作记录到 replication buffer 中。
第三阶段:发送新写命令
RDB 文件发送完成后,主库把 replication buffer 中积累的写命令发给从库,从库重新执行这些操作,主从数据达成一致。
主从级联模式
全量复制对主库有两个耗时操作:生成 RDB 和传输 RDB。如果从库数量多,主库要为每个从库 fork 子进程生成 RDB,会阻塞主线程,同时大量传输也会占满网络带宽。
解决方案是"主-从-从"级联模式:选择一个资源配置较高的从库作为级联节点,让部分从库与这个级联从库建立主从关系:
replicaof <级联从库IP> 6379

这样,级联从库承担了一部分 RDB 生成和传输的压力,主库的负载大幅降低。
基于长连接的命令传播
全量复制完成后,主从库之间维护一个长连接。主库后续收到的写命令通过这个连接持续同步给从库,避免频繁建立连接的开销。
网络断连后的增量复制
Redis 2.8 之前,网络断连后从库只能重新做全量复制,开销极大。从 2.8 开始引入了增量复制机制。
核心组件是 repl_backlog_buffer——一个环形缓冲区。主库会把所有写命令写入这个缓冲区,并记录自己的写位置 master_repl_offset;从库记录自己已读到的位置 slave_repl_offset。

网络恢复后,从库发送 psync 命令并带上自己的 slave_repl_offset。主库比较两个 offset 的差距,只把差异部分的命令同步给从库,完成增量复制。

repl_backlog_size 的设置
因为是环形缓冲区,写满后会覆盖旧数据。如果从库读取速度跟不上,未读数据被覆盖就会导致数据不一致,此时只能退化为全量复制。
缓冲空间计算公式:
缓冲空间 = (主库写入速度 - 网络传输速度) × 操作大小
repl_backlog_size = 缓冲空间 × 2
例如主库每秒写入 2000 个操作(每个 2KB),网络每秒传输 1000 个操作,则需要缓冲 1000 个操作 = 2MB,建议设置 repl_backlog_size 为 4MB。
为什么用 RDB 而不用 AOF 同步
主从同步选择 RDB 而非 AOF,原因有三:
- RDB 是压缩的二进制数据,文件小,传输快
- 从库加载 RDB 直接按协议解析还原,速度远快于 AOF 逐条重放命令
- 使用 RDB 同步不要求从库开启 AOF 功能,避免了 AOF 刷盘策略对性能的影响
实践建议
- 单个 Redis 实例数据量控制在几 GB 级别,减少 RDB 生成和传输开销
- 从库较多时采用"主-从-从"级联模式分担主库压力
- 合理调大
repl_backlog_size,降低网络波动时触发全量复制的风险 - 如果并发写入量极大,可考虑切片集群分担单主库压力

更多推荐


所有评论(0)