引言
单个Redis实例存在风险:硬件故障、数据丢失、无法扩展读能力。**主从复制(Master-Slave Replication)**是Redis高可用的基础。
今天我们深入剖析Redis主从复制的实现原理。
一、主从复制概述
1.1 什么是主从复制?
主节点(Master) 从节点(Slave1、Slave2)
↓ ↓
[写操作] SET key value [只读] GET key
↓ ↑
自动同步数据 ───────────────────────┘
特点:
- 主节点:可读可写
- 从节点:只读(默认)
- 数据自动同步:主 → 从
1.2 应用场景
- 读写分离:主写从读,提升读性能
- 数据备份:从节点作为数据备份
- 高可用:主节点宕机,从节点可提升为主
- 异地容灾:从节点部署在不同机房
二、配置主从复制
2.1 基本配置
# 从节点配置
redis-server --port 6380 --replicaof 127.0.0.1 6379
# 或修改redis.conf
replicaof 127.0.0.1 6379 # 指定主节点
2.2 运行时配置
# 从节点执行
127.0.0.1:6380> REPLICAOF 127.0.0.1 6379
OK
# 取消复制
127.0.0.1:6380> REPLICAOF NO ONE
OK # 变为独立节点
2.3 查看复制状态
# 主节点
127.0.0.1:6379> INFO replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=1234
slave1:ip=127.0.0.1,port=6381,state=online,offset=1234
# 从节点
127.0.0.1:6380> INFO replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
三、复制流程
3.1 全量复制(Full Resynchronization)
触发时机:
- 首次连接主节点
- 断线后重连,且偏移量丢失
流程:
从节点 主节点
| |
| 1. PSYNC ? -1 |
|──────────────────────────────>|
| | 2. 开始BGSAVE
| | 3. 生成RDB文件
| 4. +FULLRESYNC <runid> <offset>|
|<──────────────────────────────|
| 5. 接收RDB文件 |
|<══════════════════════════════| (大文件传输)
| 6. 清空旧数据 |
| 7. 载入RDB |
| |
| 8. 接收积压的命令 |
|<══════════════════════════════|
| 9. 完成同步 |
3.2 增量复制(Partial Resynchronization)
触发时机:
- 短暂断线后重连
- 偏移量仍在积压缓冲区内
流程:
从节点 主节点
| |
| 1. PSYNC <runid> <offset> |
|──────────────────────────────>|
| | 2. 检查offset是否在缓冲区
| 3. +CONTINUE |
|<──────────────────────────────|
| 4. 接收增量命令 |
|<══════════════════════════════|
| 5. 完成同步 |
3.3 命令传播(Command Propagation)
正常运行时:
客户端发送命令:SET key value
↓
主节点执行命令
↓
记录到积压缓冲区
↓
异步发送给所有从节点
↓
从节点执行命令
四、PSYNC机制
4.1 核心概念
runid(运行ID):
- 每个Redis实例的唯一标识
- 重启后变化
replication offset(复制偏移量):
- 主从双方都维护
- 记录已复制的字节数
- 用于判断数据一致性
replication backlog(积压缓冲区):
- 固定大小的环形缓冲区(默认1MB)
- 保存最近的写命令
- 用于增量复制
4.2 PSYNC命令
# 首次同步
PSYNC ? -1
# 重连同步
PSYNC <master_runid> <offset>
# 主节点响应:
+FULLRESYNC <runid> <offset> # 全量复制
+CONTINUE # 增量复制
-ERR # 错误
4.3 积压缓冲区
主节点写命令:
SET key1 v1 → offset=0
SET key2 v2 → offset=10
SET key3 v3 → offset=20
↓
[===== 积压缓冲区(1MB)=====]
| offset=0: SET key1 v1 |
| offset=10: SET key2 v2 |
| offset=20: SET key3 v3 |
| ... |
[=============================]
从节点断线重连:
- 如果offset=20仍在缓冲区 → 增量复制
- 如果offset已被覆盖 → 全量复制
配置参数:
# 积压缓冲区大小
repl-backlog-size 1mb
# 缓冲区保留时间(从节点全部断开后)
repl-backlog-ttl 3600
五、性能优化
5.1 避免全量复制
优化策略:
增大积压缓冲区:
repl-backlog-size 10mb # 根据写入速度调整减少断线概率:
# 从节点配置 repl-timeout 60 # 超时时间 repl-ping-slave-period 10 # 心跳间隔避免主节点重启:
- 使用持久化恢复数据
- 避免runid变化
5.2 主节点优化
# 1. 关闭RDB持久化(由从节点备份)
save ""
# 2. 开启无磁盘复制(适合高速网络)
repl-diskless-sync yes
# 3. 限制同时同步的从节点数量
repl-diskless-sync-delay 5 # 延迟5秒,收集多个从节点
5.3 从节点优化
# 1. 只读模式(默认)
replica-read-only yes
# 2. 优先级(用于选主)
replica-priority 100 # 越小优先级越高
# 3. 禁用持久化(如果仅用于读)
save ""
appendonly no
六、常见问题
6.1 复制风暴
问题:
主节点宕机重启 → runid变化
↓
所有从节点全量复制
↓
主节点负载暴增(N个RDB + 网络传输)
解决:
- 分批重启从节点
- 使用持久化恢复主节点数据(保持runid)
- 使用哨兵自动故障转移
6.2 数据延迟
问题:主从数据不一致
原因:
- 网络延迟
- 从节点处理慢
检测:
# 查看延迟(字节数)
127.0.0.1:6379> INFO replication
master_repl_offset:10000 # 主节点
slave0:...,offset=9500 # 从节点落后500字节
优化:
- 使用Pipeline批量操作
- 避免慢命令(KEYS、FLUSHALL)
- 升级网络带宽
6.3 主从切换
问题:主节点宕机,如何切换?
手动切换:
# 1. 选择一个从节点
127.0.0.1:6380> REPLICAOF NO ONE # 变为主节点
# 2. 其他从节点指向新主
127.0.0.1:6381> REPLICAOF 127.0.0.1 6380
自动切换:使用哨兵(Sentinel)
七、总结
核心要点
主从复制作用
- 读写分离、数据备份
- 高可用基础
复制类型
- 全量复制:首次或offset丢失
- 增量复制:短暂断线,offset在缓冲区
PSYNC机制
- runid:主节点标识
- offset:复制进度
- backlog:积压缓冲区
性能优化
- 增大积压缓冲区
- 无磁盘复制
- 分批同步
常见问题
- 复制风暴:分批重启
- 数据延迟:监控offset
- 主从切换:使用哨兵
下一篇预告
主从复制解决了数据备份问题,但如何实现自动故障转移?哨兵(Sentinel)如何工作?
下一篇《哨兵Sentinel:高可用方案》,我们将深入哨兵机制。