高 进阶
Redis主从复制#
一句话答案#
全量复制(RDB 快照)+ 增量复制(repl_backlog 缓冲区),默认异步复制——主从切换时可能丢失未同步的数据。
核心要点
复制流程#
从库发送 PSYNC runId offset
├── 首次连接(runId=?,offset=-1)→ 全量复制
└── 重连 → 判断 offset 是否在 repl_backlog 中
├── 在 → 增量复制
└── 不在 → 全量复制plaintext全量复制#
1. 从库发送 PSYNC ? -1
2. 主库执行 BGSAVE 生成 RDB 快照(fork 子进程)
3. 主库发送 RDB 文件给从库
4. 期间主库的写命令暂存到 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 秒内的写入命令
- 高写入场景(如 1 万 QPS)→ 最多丢 1 万条
减少数据丢失的配置#
# 至少 N 个从库确认写入,否则主库拒绝写入
min-replicas-to-write 1
# 从库复制延迟超过 M 秒,视为不可用
min-replicas-max-lag 10plaintext效果: 如果没有任何从库在 10 秒内确认同步,主库停止接受写入 → 牺牲可用性换数据安全。
注意: 这不是同步复制,只是”确保至少有一个从库在跟上”。写入仍然是先返回客户端的。
主从延迟的排查#
# 在从库上执行
INFO replication
# 关注:
master_link_status: up # 连接状态
master_last_io_seconds_ago: 0 # 上次 IO 距今秒数
slave_repl_offset: 1234567 # 从库 offset
master_repl_offset: 1234580 # 主库 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 不在 / runId 不匹配
- “repl_backlog 太小会怎样?”→ 频繁触发全量复制,主库 fork + 传输 RDB 开销大
- “主从切换丢数据怎么办?”→ min-replicas-to-write 降低丢失量 + 业务层做对账补偿
- “主从复制和 Cluster 的数据同步一样吗?”→ 一样,Cluster 的每个分片内部也是主从复制
易错点:
- ❌ “主从复制是同步的”——默认异步,主库不等从库确认
- ❌ “从库宕机不影响主库”——如果配了 min-replicas-to-write,从库全挂会导致主库拒绝写入
- ❌ “Redis 不会丢数据”——异步复制 + 主从切换,最多丢最近 1 秒的写入
- ❌ 混淆 repl_backlog 和 client output buffer——前者是全局复制缓冲,后者是每个从库的发送缓冲