面试知识库
高 进阶

Redis主从复制#

一句话答案#

全量复制(RDB 快照)+ 增量复制(repl_backlog 缓冲区),默认异步复制——主从切换时可能丢失未同步的数据。

核心要点

复制流程#

从库发送 PSYNC replid offset(4.0 PSYNC2 起用 replid,之前是主库 runId)
  ├── 首次连接(replid=?,offset=-1)→ 全量复制
  └── 重连 → 判断 offset 是否在 repl_backlog 中
        ├── 在 → 增量复制
        └── 不在 → 全量复制
plaintext

全量复制#

1. 从库发送 PSYNC ? -1
2. 主库 fork 子进程生成 RDB 快照
3. 主库发送 RDB 给从库(7.0 起 repl-diskless-sync 默认 yes:子进程直接写 socket,不落盘)
4. 期间主库的写命令暂存到该从库的复制缓冲区(replica client output buffer;7.0 起与 repl_backlog 共用一份全局复制缓冲)
5. 从库加载 RDB
6. 主库发送缓冲区中的增量命令
7. 从库执行增量命令,同步完成
plaintext

开销: fork 主进程(内存翻倍风险)+ 网络传输 RDB + 从库加载 RDB 期间阻塞

增量复制#

  • 主库维护一个环形缓冲区 repl_backlog(默认 1MB)
  • 从库断连重连后,发送自己的 offset
  • 主库从 repl_backlog 中找到 offset 之后的命令增量发送
  • repl_backlog 太小 → offset 被覆盖 → 退化为全量复制

生产建议: repl-backlog-size 设为 写入 QPS × 平均命令大小 × 可容忍断连秒数 × 2

异步复制与数据丢失#

默认行为: 主库写入后立即返回客户端,不等待从库确认。

数据丢失场景:

时刻 T1: 客户端写入主库 SET key=100,主库返回 OK
时刻 T2: 主库宕机(key=100 尚未同步到从库)
时刻 T3: 哨兵提升从库为新主库
结果: key=100 丢失
plaintext

丢失量估算:

  • 主从延迟通常 < 1 秒
  • 通常丢失 最近约 1 秒内的写入命令(丢多少取决于切换时的复制延迟,网络抖动/大 Key 时可能更多)
  • 高写入场景(如 1 万 QPS)→ 延迟 1 秒约丢 1 万条

减少数据丢失的配置#

# 至少 N 个从库的 ACK 延迟 ≤ min-replicas-max-lag,否则主库拒绝写入
min-replicas-to-write 1

# 从库复制延迟超过 M 秒,视为不可用
min-replicas-max-lag 10
plaintext

效果: 如果没有任何从库在 10 秒内确认同步,主库停止接受写入 → 牺牲可用性换数据安全。

注意: 这不是同步复制,只是”确保至少有一个从库在跟上”。写入仍然是先返回客户端的。

主从延迟的排查#

# 在从库上执行 INFO replication,看连接状态:
master_link_status: up          # 连接状态
master_last_io_seconds_ago: 0   # 上次 IO 距今秒数

# 在主库上执行 INFO replication,算延迟:
master_repl_offset: 1234580                          # 主库 offset
slave0: ip=...,state=online,offset=1234567,lag=0     # 从库 offset(两者差值 = 延迟字节数)
bash

延迟大的常见原因:

  • 从库负载高(被大量读请求打满)
  • 网络带宽不足
  • 从库执行慢命令(如 KEYS *)
  • 主库大 Key 写入(从库重放慢)

面试回答(2分钟版)

Redis 主从复制分为全量同步和增量同步。从库首次连接或 repl_backlog 中找不到断点 offset 时触发全量同步:主库 fork 子进程生成 RDB 快照发给从库,期间写命令暂存到缓冲区,从库加载完 RDB 再接收增量命令。断线重连时如果 offset 还在 repl_backlog 环形缓冲区内就做增量同步,只发送断点之后的命令,非常轻量。正常运行阶段是命令传播,主库每条写命令异步发给所有从库。关键点是默认异步复制,主库写入成功即返回客户端不等从库确认,所以主从切换时通常会丢失最近约一秒(取决于复制延迟)的写入。要降低丢失风险可以配置 min-replicas-to-write 和 min-replicas-max-lag,确保至少有一个从库在跟上,否则主库拒绝写入。repl_backlog 大小建议按”写入 QPS 乘以命令大小乘以可容忍断连秒数”来设置,太小会频繁触发全量同步。

追问与易错

追问方向:

  • “全量复制什么时候触发?”→ 首次连接 / repl_backlog offset 不在 / replid 不匹配(4.0 PSYNC2 起新主会保留旧主 replid2,主从切换后其他从库仍可部分重同步)
  • “repl_backlog 太小会怎样?”→ 频繁触发全量复制,主库 fork + 传输 RDB 开销大
  • “主从切换丢数据怎么办?”→ min-replicas-to-write 降低丢失量 + 业务层做对账补偿
  • “主从复制和 Cluster 的数据同步一样吗?”→ 一样,Cluster 的每个分片内部也是主从复制

易错点:

  • ❌ “主从复制是同步的”——默认异步,主库不等从库确认
  • ❌ “从库宕机不影响主库”——如果配了 min-replicas-to-write,从库全挂会导致主库拒绝写入
  • ❌ “Redis 不会丢数据”——异步复制 + 主从切换,会丢切换时尚未复制的写入(通常约 1 秒内)
  • ❌ 混淆 repl_backlog 和 client output buffer——前者用于断线重连的部分同步,后者是每个从库的发送缓冲(受 client-output-buffer-limit replica 限制,超限会断开从库);7.0 起两者底层共用一份全局复制缓冲