主从复制:全量同步 + 增量同步
一句话总结
第一次连从库 → 全量同步(主库把全部数据发给从库)。之后日常同步 → 增量同步(主库把写命令实时发给从库)。全量只在”第一次”或”断线太久追不上”时发生,平时靠增量。
🌰 理解主从复制
你是一家奶茶店的总店长(主库),开了一家分店(从库)。
总店长(主库)
└─ 顾客来买奶茶,你收钱出杯
└─ 每做一杯你都同步告诉分店
分店(从库)
└─ 刚开业时:总店把全部菜单和配方拿给分店(全量同步)
└─ 日常营业:卖出一杯就在群里通知一声(增量同步)
两种同步:
-
全量同步 = 从库第一次上线或掉线太久,主库把全部数据发过去
-
增量同步 = 主库每次写操作,实时发给从库
一、全量同步(Full Sync)
二、增量同步(Partial Sync / Incremental Replication)
什么时候触发
1. 从库第一次连上主库(刚搭建好复制)
2. 从库断线太久,主库的"命令缓冲区"(repl_backlog)已经覆盖了
从库最后同步的位置 → 从库跟不上 → 必须重新全量
全量同步的流程
sequenceDiagram participant Slave as 从库 participant Master as 主库 Slave->>Master: PSYNC ? -1("我啥都没有,来全量吧") Master->>Master: BGSAVE(fork 子进程生成 RDB) Note over Master: 同时把新命令存到缓冲区(replication buffer) Master->>Slave: 发送 RDB 文件 Slave->>Slave: 清空旧数据 + 加载 RDB Master->>Slave: 发送缓冲区里的新命令 Note over Slave: 补上生成 RDB 期间漏掉的数据 Note over Slave,Master: 全量同步完成 → 进入增量同步模式
全量同步的问题
RDB 文件传输是瓶颈。
假设主库有 10GB 数据:
└─ BGSAVE 生成 RDB → 几秒到几十秒(期间 fork 耗内存)
└─ 传输 RDB 给从库 → 取决于带宽(1Gbps 约 80 秒)
└─ 从库加载 RDB → 几十秒
└─ 整个过程主库不能停写
总耗时:可能 2-5 分钟。
如果 10 个从库同时全量同步 → 主库要 fork 10 次?不,只 fork 一次。
Redis 优化:多个从库同时请求全量同步时,
主库只 fork 一次生成 RDB,发给所有从库。
什么时候触发
全量同步完成后 → 进入增量同步模式。
只要从库没断线太久(还在 repl_backlog 范围内),就一直增量。
增量同步的流程
主库处理一条写命令:
SET name 'Alice'
│
↓
Step 1: 主库把命令写入 repl_backlog(环形缓冲区,固定大小)
└─ 同时更新 master_repl_offset(偏移量,当前写到了第几个字节)
│
↓
Step 2: 主库把命令发给所有连接的从库
└─ "SET name 'Alice'"
│
↓
Step 3: 从库收到命令,执行
└─ 更新自己的数据
└─ 更新 slave_repl_offset(自己同步到了哪个位置)
│
↓
Step 4: 从库告诉主库:"我已经同步到这个位置了"
└─ 主库记录从库的 offset,下次断线重连时用
断线重连后的增量同步
从库断线了 30 秒,又连上了。
从库发送 PSYNC <主库ID> <从库最后的位置 offset>
主库检查:
├─ offset 还在 repl_backlog 里? → 增量同步 ✅
│ 只发从库落下的那部分命令
│
└─ offset 已经被 repl_backlog 覆盖了? → 重新全量同步 ❌
从库掉线太久,追不上了
repl_backlog 是什么
graph LR subgraph "repl_backlog(环形缓冲区)" CMD1["写命令 1"] --> CMD2["写命令 2"] --> CMD3["写命令 3"] --> CMDN["..."] end OVERWRITE["满了从开头覆盖"] -.-> CMD1
大小由 repl-backlog-size 决定(默认 1MB)
如果从库断线时间 < 缓冲区存满的时间 → 增量同步
如果从库断线时间 > 缓冲区存满的时间 → 全量同步
增量 vs 全量对比
| 维度 | 全量同步 | 增量同步 |
|---|---|---|
| 触发时机 | 从库第一次连接 / 断线太久 | 日常运行 / 短时间断线重连 |
| 传输内容 | 整个 RDB 文件(全部数据) | 几条写命令 |
| 数据量 | GB 级 | KB 级 |
| 耗时 | 分钟级 | 毫秒级 |
| 对主库影响 | fork 子进程 + 磁盘 IO + 网络带宽 | 极小(只多一次网络发送) |
| 对从库影响 | 清空所有数据 + 重新加载 | 立即执行几条命令 |
| 网络要求 | 带宽要大,否则传输慢 | 基本没要求 |
一个完整的主从复制生命周期
graph TD START["从库启动"] --> CONN["连接主库<br/>(建立 TCP 连接)"] CONN --> AUTH["身份认证<br/>(如果主库设置了 requirepass)"] AUTH --> PSYNC["PSYNC ? -1<br/>(第一次连,发送全量请求)"] subgraph "全量同步阶段" FULL["主库 BGSAVE → 生成 RDB<br/>发送 RDB 给从库<br/>从库清空旧数据 + 加载 RDB<br/>主库补发缓冲区里的增量命令"] end PSYNC --> FULL subgraph "增量同步阶段" INCR["主库每执行一条写命令:<br/>1. 写 repl_backlog<br/>2. 发给从库执行<br/>从库实时接收并执行"] BRANCH{"如果从库断线"} INCR --> BRANCH BRANCH -->|"短时间重连"| INCR BRANCH -->|"长时间断线"| FULL end FULL --> INCR
常问:主从复制的注意事项
1. 主库写了,从库看不到?
主库:SET name 'Alice'
从库:GET name → (nil)
检查:
└─ 从库有延迟(从库还在同步上一条命令)
└─ 正常现象,等一会儿就好
└─ 如果一直不同步 → 检查复制状态(INFO replication)
2. 从库崩溃了再恢复?
如果断线不久 → 自动增量同步 ✅
如果断线太久 → 自动全量同步 Redis 2.8+ 支持
老版本 Redis(2.8 之前):
断线重连也是全量同步 → 非常低效
3. repl_backlog 配多大?
repl-backlog-size = 主库每秒写入量 × 能接受的最大断线时间
比如主库每秒写 1MB,希望从库断线 1 小时内能增量同步:
backlog = 1MB × 3600 = 3.6GB(记得配大一点)
默认 1MB 太小了。大一点,避免频繁全量同步。
4. 主从复制和哨兵的区别?
主从复制:数据同步(主库写→从库读)
哨兵:监控 + 自动故障转移(主库挂了→选一个从库变主库)
主从复制是哨兵的基础,哨兵是主从复制的增强。
记忆口诀
第一次连或断太久 → 全量(发整个 RDB)。日常同步 → 增量(发写命令)。
全量很重(fork+RDB+传输),增量很轻(几行命令)。
repl_backlog 是关键——够大才能增量,太小逼你全量。
从库崩了不用慌——短连增量,长连全量,Redis 能自己处理。
速记卡(面试闪卡)
Q1:一句话讲清「主从复制:全量同步 + 增量同步」到底是什么?
A:第一次连从库 → 全量同步(主库把全部数据 RDB 发给从库)。之后日常 → 增量同步(主库把写命令实时发给从库)。全量只在”第一次”或”断线太久追不上”时发生,平时靠增量。
Q2:理解主从复制 —— 怎么理解?
A:你是奶茶总店长(主库 Master),开了一家分店(从库 Slave)。总店每做一杯都在群里通知一声,分店照着做,这就是复制。分店刚开业时,总店把全部菜单配方拿过去(全量同步);日常营业每卖出一杯就通知一声(增量同步)。主库负责写,从库负责读,一写多读扛住高并发读流量。
Q3:一、全量同步(Full Sync) —— 怎么理解?
A:触发时机:从库第一次连上主库,或从库断线太久、主库的 repl_backlog(复制积压缓冲区,一个固定大小的环形 buffer)已经覆盖了从库最后同步的位置。流程:从库发 PSYNC ? -1(“我啥都没有,来全量吧”)→ 主库 BGSAVE(Background SAVE,fork 子进程生成 RDB 快照文件)的同时把新命令存进 replication buffer → 发 RDB 给从库 → 从库清空旧数据加载 RDB → 主库补发缓冲区里 RDB 生成期间漏掉的新命令。瓶颈在 RDB 传输:10GB 数据按 1Gbps 带宽约 80 秒,总耗时可能 2 到 5 分钟。多个从库同时请求时主库只 fork 一次,RDB 发给所有从库。
Q4:二、增量同步(Partial Sync) —— 怎么理解?
A:全量完成后进入增量模式:主库每执行一条写命令,先写进 repl_backlog 并更新 master_repl_offset(主库写到了第几个字节),再发给所有从库,从库执行后更新自己的 slave_repl_offset。从库断线 30 秒又连上,发 PSYNC <主库ID> <自己的offset>:只要 offset 还在 repl_backlog 里 → 增量,只补落下的命令;offset 已被覆盖 → 重新全量。repl_backlog 大小由 repl-backlog-size 决定(默认 1MB),公式:backlog = 主库每秒写入量 × 能接受的最大断线时间,默认 1MB 太小,容易逼出全量。
Q5:常问:主从复制注意事项 —— 怎么理解?
A:①主库写了从库看不到?正常,从库有同步延迟,等一会儿就好,一直不同步查 INFO replication。②从库崩了?断线不久自动增量,断线太久自动全量(Redis 2.8+ 支持,老版本断线重连也是全量,很低效)。③repl_backlog 配多大?按上面公式算,配大点避免频繁全量。④主从复制和哨兵(Sentinel)区别?主从复制负责数据同步(主写从读),哨兵负责监控 + 自动故障转移(主挂了选从变主),主从是哨兵的基础,哨兵是主从的增强。
Q6:核心速记主线有哪些?
A:一、全量同步(RDB + BGSAVE + replication buffer,分钟级)、二、增量同步(repl_backlog 环形缓冲 + offset,毫秒级)、三、断线重连靠 offset 是否在 backlog 里决定增量还是全量、四、repl_backlog-size 配置、五、主从复制 vs 哨兵。
口诀
A:首次连接或断太久,全量 RDB 发一遍;
日常同步靠增量,写命令实时传;
repl_backlog 是关卡,够大才增量、太小逼全量;
主从同步哨兵管转移,二者配合才稳当。