Redis主从复制原理
Redis 的五种数据结构各有特色,用对了才能发挥它的优势。很多人只用到了 String 和 Hash,却不知道 List、Set、ZSet 在特定场景下更合适。本文从应用场景出发,讲什么时候用什么类型。
一、复制架构
#
1.1 基本架构
┌─────────┐ 复制 ┌─────────┐ │ Master │ ──────────> │ Slave │ │ (读写) │ │ (只读) │ └─────────┘ └─────────┘
|
#
1.2 一主多从
┌──────────> ┌─────────┐ ┌────────┐ │ Slave 1 │ │ Master │ ──────────>├─────────┤ └────────┘ │ Slave 2 │ └──────────> │ Slave 3 │ └─────────┘
|
#
1.3 级联复制
Master ──> Slave1 ──> Slave2 │ └────> Slave3
|
二、复制流程
#
2.1 全量同步(Full Resynchronization)
Slave Master │ │ │ 1. 连接Master │ │ ──────────────────────> │ │ │ │ 2. 发送PSYNC命令 │ │ ──────────────────────> │ │ │ │ 3. 返回+FULLRESYNC │ │ <────────────────────── │ │ runid │ │ offset │ │ │ │ 4. 发送RDB文件 │ │ <────────────────────── │ │ │ │ 5. 加载RDB │ │ │ │ 6. 接收后续写命令 │ │ <────────────────────── │ │ (复制积压缓冲区) │
|
详细步骤:
Slave连接Master
replicaof 192.168.1.100 6379
|
发送PSYNC命令
PSYNC <runid> <offset>
首次复制: PSYNC ? -1
断线重连: PSYNC <master_runid> <slave_offset>
|
Master响应
- 如果runid匹配且offset在复制积压缓冲区内:返回
+CONTINUE(部分重同步)
- 否则:返回
+FULLRESYNC <runid> <offset>(全量同步)
Master生成并发送RDB
Master执行BGSAVE → 生成RDB文件 → 发送给Slave
|
Slave加载RDB
Slave清空内存 → 加载RDB文件 → 数据与Master一致
|
同步后续写命令
Master将RDB生成后的写命令写入复制积压缓冲区 Slave接收并执行这些命令
|
#
2.2 增量同步(Partial Resynchronization)
Slave Master │ │ │ 网络断开 │ │ XXXXXXXXXXXXXXXXXXXXX │ │ │ │ 网络恢复 │ │ │ │ 发送PSYNC runid offset │ │ ──────────────────────> │ │ │ │ 3. 返回+CONTINUE │ │ <────────────────────── │ │ │ │ 4. 发送缺失的命令 │ │ <────────────────────── │
|
触发条件:
- Slave之前同步过该Master(runid匹配)
- Slave的offset在Master的复制积压缓冲区内
三、核心机制
#
3.1 复制积压缓冲区(Replication Backlog)
Master维护一个固定大小的FIFO队列:
┌─────────────────────────────────────────────────────────────┐ │ 复制积压缓冲区(默认1MB) │ │ │ │ offset:1000 offset:1001 offset:1002 offset:1200 │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ SET a 1 │ │ SET b 2 │ │ DEL c │ ... │ SET x 9 │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ master_repl_offset = 1200 │ └─────────────────────────────────────────────────────────────┘
Slave断线后重连,offset=1001: - 1001在缓冲区内(1000~1200) - 只需发送1001~1200的命令
Slave断线后重连,offset=500: - 500不在缓冲区内(已超出) - 触发全量同步
|
配置:
# 复制积压缓冲区大小(建议根据写入量调整) repl-backlog-size 10mb
# Slave断线后,Master保持缓冲区的超时时间 repl-backlog-ttl 3600
|
#
3.2 PSYNC机制
Redis 2.8之前使用SYNC,只能全量同步。
Redis 2.8+ 使用PSYNC,支持增量同步。
void syncCommand(client *c) { if (!strcasecmp(c->argv[0]->ptr, "psync")) { if (masterTryPartialResynchronization(c) == C_OK) { return; } } }
|
#
3.3 Master的RunID
RunID是40字符的随机十六进制字符串: - Master启动时生成 - 用于标识Master实例 - Slave记录Master的RunID
作用: - 判断Slave是否之前同步过该Master - 如果RunID不同,说明Master重启过,必须全量同步
|
#
3.4 传播写命令
Master执行写命令:
1. 修改内存数据 2. 写入AOF(如果开启) 3. 传播给所有Slave: 对于每个Slave: - 写入Slave的输出缓冲区 - 等待Slave的ACK(如果配置了min-slaves)
|
四、配置
#
4.1 Slave配置
# redis.conf (Slave)
# 方式一:配置文件中指定Master replicaof 192.168.1.100 6379
# 方式二:启动参数 # redis-server --replicaof 192.168.1.100 6379
# 方式三:运行时配置 # redis-cli REPLICAOF 192.168.1.100 6379
# 如果Master有密码 masterauth password
# Slave只读(默认) replica-read-only yes
# 复制优先级(Sentinel使用,数值小的优先提升为Master) replica-priority 100
|
#
4.2 Master配置
# redis.conf (Master)
# 允许Slave数量(用于延迟计算) min-replicas-to-write 1 min-replicas-max-lag 10
# 复制积压缓冲区 repl-backlog-size 10mb repl-backlog-ttl 3600
# 禁用TCP_NODELAY(减少网络小包) repl-disable-tcp-nodelay no
|
#
4.3 无磁盘复制(Diskless Replication)
Redis 2.8.18+ 支持直接通过网络发送RDB:
# Master直接通过网络发送RDB,不写入磁盘 repl-diskless-sync yes
# 延迟开始发送,等待多个Slave连接(减少重复RDB生成) repl-diskless-sync-delay 5
|
适用场景:
五、复制状态监控
#
5.1 查看复制状态
redis-cli INFO replication
redis-cli INFO replication
|
#
5.2 监控复制延迟
MASTER_OFFSET=$(redis-cli -h master INFO replication | grep master_repl_offset | cut -d: -f2) SLAVE_OFFSET=$(redis-cli -h slave INFO replication | grep slave_repl_offset | cut -d: -f2)
LAG=$(($MASTER_OFFSET - $SLAVE_OFFSET)) echo "复制延迟: $LAG bytes"
if [ $LAG -gt 1000000 ]; then echo "复制延迟过大!" | mail -s "Redis复制告警" admin@company.com fi
|
#
5.3 查看Slave客户端
redis-cli CLIENT LIST | grep slave
|
六、常见问题
#
6.1 复制延迟
原因:
- 网络带宽不足
- Master写入量过大
- Slave性能差(单线程执行命令)
- 大KEY操作阻塞
解决:
ping slave_ip
redis-cli SLOWLOG GET 10
CONFIG SET repl-backlog-size 100mb
|
#
6.2 全量同步频繁触发
原因:
- 复制积压缓冲区太小
- Slave频繁断开重连
- 网络不稳定
解决:
CONFIG SET repl-backlog-size 100mb
redis-cli INFO stats | grep sync
|
#
6.3 主从数据不一致
排查:
redis-cli INFO replication | grep offset
redis-cli GET key
redis-cli GET key
redis-cli CONFIG GET replica-read-only
|
#
6.4 复制中断
tail -f /var/log/redis/redis-server.log
|
七、复制优化
#
7.1 树状复制
Master ──> Slave1 ──> Slave2 │ └────> Slave3 ──> Slave4
优势:减少Master的复制压力 劣势:级联延迟增加
|
#
7.2 复制过滤
# Slave只复制指定数据库(不推荐) replicate-do-db 0
# Slave忽略指定命令(Redis 7.0+) replica-ignore-disk-write-errors yes
|
#
7.3 读写分离
@Configuration public class RedisConfig { @Bean public LettuceConnectionFactory redisConnectionFactory() { RedisStaticMasterReplicaConfiguration config = new RedisStaticMasterReplicaConfiguration("master", 6379); config.addNode("slave1", 6379); config.addNode("slave2", 6379); return new LettuceConnectionFactory(config); } }
|
八、总结
| 同步方式 |
触发条件 |
数据量 |
对Master影响 |
| 全量同步 |
首次复制、offset不在缓冲区 |
全部数据 |
大(BGSAVE) |
| 增量同步 |
断线重连、offset在缓冲区 |
部分命令 |
小 |
| 配置项 |
推荐值 |
说明 |
| repl-backlog-size |
10-100MB |
根据写入量调整 |
| repl-diskless-sync |
yes |
无磁盘复制 |
| min-replicas-to-write |
1-2 |
保证至少N个Slave |
Redis主从复制的核心要点:
- PSYNC支持增量同步,减少全量同步
- 复制积压缓冲区大小决定断线重连效率
- 监控复制延迟,及时发现问题
- 避免大KEY操作导致复制阻塞
- 考虑树状复制减轻Master压力
核心要点
String:简单的键值对,适合缓存、计数器
Hash:存储对象属性,适合用户信息、配置
List:有序列表,适合消息队列、最新列表
Set:无序去重,适合共同好友、抽奖
ZSet:有序集合,适合排行榜、积分系统
总结
选择合适的数据结构是使用 Redis 的关键。在实际项目中,根据业务需求选择合适的类型,可以提升性能和开发效率。