Redis 主从复制的实现原理是什么?
- 标签:后端、Redis
🏛 业务架构师
Redis 主从复制流程分两个阶段:
全量同步(首次或断线太久)
- 从库 psync 带复制 ID,主库判断无法增量 → 执行 bgsave 生成 RDB 发给从库(期间新写命令暂存复制缓冲区);
- 从库清空旧数据、载入 RDB;
- 主库把缓冲区期间的增量命令发去,从库回放——进入实时复制。
增量同步(短断线恢复)
- 主库为每个连接维护环形复制积压缓冲区(repl_backlog),从库断线重连时上报偏移量,偏移量还在环形缓冲内就只补差量(PSYNC 部分)——断线太久偏移被覆盖,退回全量。
业务视角要管理的三件事:
- 复制延迟是常态:异步复制下从库永远略滞后——写后立读的关键查询路由主库(支付结果页),报表类容忍延迟走从库——按业务分级设计读策略;
- 全量同步的风暴:从库重启/网络抖动触发全量 → 主库 bgsave(CPU/内存/磁盘三重压力)+ 大 RDB 传输——多从库同时全量=主库被拖垮。对策:控制从库数量、复制缓冲区容量按写入量调大、从库分级级联(sub-slave)分摊;
- 数据丢失窗口:异步复制 + 主库宕机 = 最后一批未同步的写丢失——支付级业务上半同步复制或客户端兜底(写成功确认前不算成功)。
演进定位:主从+哨兵解决高可用(容量内);容量瓶颈才上 Cluster——主从是"够用的艺术",别为规模没到的问题提前付费。
🏗 系统架构师
Redis 主从复制我按"建立 → 同步 → 命令传播"三阶段讲:
1. 建立连接:从库执行 replicaof(旧 slaveof)指向主库,与主库建立长连接,发送 psync(带 replicationid + offset)。
2. 数据同步(两种情形):
- 全量复制(首次/断连太久):主库 bgsave 生成 RDB 发给从库(期间新写命令存** replication buffer / backlog**),从库清空旧数据 → 载入 RDB → 再接收缓冲区增量命令重放。代价极高:fork、RDB 传输、从库加载,GB 级实例分钟级——所以要尽量避免不必要的全量;
- 增量复制(短断连重连):psync 上报 offset,若主库环形缓冲区 repl_backlog(默认 1MB,生产建议调大) 还覆盖该 offset,只补发缺失区间;否则退化为全量——backlog 大小决定"可容忍的断连窗口"。
3. 命令传播:正常期主库把写命令异步实时发给从库,从库执行保持跟随;主库周期 PING 保活。
架构要点(工程必聊):
- 异步复制:主库不等从库 ack——低延迟但故障切换可能丢最后一批写入(与 MySQL 半同步对比);
- 复制积压不足/从库太多 → 风暴问题:级联复制(从库再挂从库)分摊;
- 全量复制触发条件要监控(master_replid 变更、offset 超界),频繁全量是容量事故前兆;
- 主从 + 哨兵/Cluster 才构成完整高可用。
💻 全栈开发专家
Redis 主从复制分全量同步和增量同步两条路径:
全量同步(第一次连接或断开太久):
text
1. 从库发 PSYNC <replid> <offset>,主库发现无法增量 → +FULLRESYNC
2. 主库 bgsave 生成 RDB(期间新写命令存"复制缓冲区")
3. RDB 传给从库,从库清空旧数据后载入
4. 主库把缓冲区期间的增量命令补发给从库 → 进入命令传播增量同步(短断线续传,2.8+):
text
主库一直把写命令同时写入"复制积压缓冲区"(repl_backlog,环形 buffer)
从库断线重连 PSYNC 带上 offset:
offset 还在 backlog 范围内 → 只补差量(增量)
已被覆盖 → 退化为全量命令传播阶段:主库每个写命令实时发给从库(异步),从库回报 offset。
工程要点(踩过的坑):
- repl-backlog-size 默认 1MB 太小:网络抖动 10 秒就可能被覆盖 → 全量同步风暴(大实例全量同步又贵又卡主库)。写多的实例我调到 128MB+;
- 主库无盘复制:
repl-diskless-sync yes,网络盘慢时 RDB 直接走网络不落盘; - 异步复制的丢失窗口:主库不等从库 ack 就回客户端,宕机丢最后一批写——要少丢用
min-replicas-to-write或 WAIT 命令; - 全量同步期间主库要 fork + 传 RDB,多从库同时重连会打爆主库,从库重连要错峰。