1. 主从复制是什么
Redis 主从复制指的是:一台 Redis 作为 master,其他 Redis 实例作为 replica,从 master 同步数据。
在旧资料里经常看到 master/slave、slaveof 这些说法。Redis 新版本更推荐使用 master/replica、REPLICAOF,不过很多命令和配置仍然保留了兼容旧写法。
主从复制示意如下:

一句话总结:master 负责写,replica 复制 master 数据,通常承担读请求或备份角色。
这里有两个边界需要先说清楚:
- replica 是否承担读请求,取决于业务读写路由和 replica-read-only 等配置。
- Redis 主从复制默认是异步复制,不提供强一致复制语义。
2. 主从复制能解决什么
主从复制主要解决三个问题:
| 问题 | 主从复制的作用 |
|---|---|
| 读压力过大 | 可以把部分读请求分散到 replica |
| 数据备份 | replica 保留 master 数据副本 |
| 高可用基础 | Sentinel 和 Cluster 的故障转移都依赖复制关系 |
但主从复制本身不等于高可用。普通主从结构下,master 宕机后 replica 不会自动升级为新 master,需要人工处理。自动故障检测、选主和 failover 通常由 Sentinel 或 Cluster 提供。
3. 常用命令
| 命令 | 说明 |
|---|---|
| INFO replication | 查看复制状态 |
| ROLE | 查看当前实例角色 |
| REPLICAOF host port | 让当前实例成为指定 master 的 replica |
| REPLICAOF NO ONE | 取消复制,让当前实例变成 master |
| SLAVEOF host port | 旧命令,仍能在很多版本中看到 |
| SLAVEOF NO ONE | 旧命令,效果类似 REPLICAOF NO ONE |
示例:
# 在 6380 实例执行,让它复制 6379
redis-cli -p 6380 --pass 111111 replicaof 127.0.0.1 6379
# 查看复制状态
redis-cli -p 6380 --pass 111111 info replication
# 让 6380 脱离主从关系,变成 master
redis-cli -p 6380 --pass 111111 replicaof no one
4. 一主二从实验架构
这里用一台机器上的三个端口举例:
| 角色 | 端口 | 配置文件 |
|---|---|---|
| master | 6379 | /myredis/redis6379.conf |
| replica-1 | 6380 | /myredis/redis6380.conf |
| replica-2 | 6381 | /myredis/redis6381.conf |

5. 配置文件方式
master 配置示例:
bind 0.0.0.0
protected-mode no
port 6379
daemonize yes
requirepass 111111
dir /myredis
logfile "/myredis/redis6379.log"
pidfile /var/run/redis_6379.pid
dbfilename dump6379.rdb
appendonly yes
appendfilename "appendonly6379.aof"
replica 配置示例:
bind 0.0.0.0
protected-mode no
port 6380
daemonize yes
requirepass 111111
masterauth 111111
replicaof 127.0.0.1 6379
replica-read-only yes
dir /myredis
logfile "/myredis/redis6380.log"
pidfile /var/run/redis_6380.pid
dbfilename dump6380.rdb
appendonly yes
appendfilename "appendonly6380.aof"
几个关键配置:
| 配置 | 说明 |
|---|---|
| replicaof host port | 指定 master |
| masterauth | replica 连接 master 使用的密码 |
| replica-read-only yes | replica 只读,默认建议开启 |
| requirepass | 当前实例对客户端的访问密码 |
如果使用 Redis 6 及以上版本,生产环境更推荐用 ACL 管理用户与权限,而不是所有客户端共享同一个 requirepass。当 master 启用 ACL 用户后,replica 侧还需要按实际认证方式配置 masteruser 和 masterauth。
replica-read-only yes 建议保持开启。如果关闭只读限制,replica 上的本地写入不会反向同步到 master,也可能在后续全量同步时被覆盖,容易造成数据不一致。
部分配置命令:
# 绑定 Redis 监听地址
# 这里注释掉 bind 127.0.0.1 -::1,表示不只限制本机访问
# bind 127.0.0.1 -::1
# Redis 进程 PID 文件路径
pidfile /var/run/redis_6379.pid
# Redis 访问密码
requirepass 111111
# 配置当前 Redis 实例作为从库,指定主库 IP 和端口
# 格式:replicaof <masterip> <masterport>
replicaof 127.0.0.1 6379
# 如果主库设置了密码,从库需要配置 masterauth
# 否则从库连接主库时会因为认证失败而无法同步数据
# 格式:masterauth <master-password>
masterauth 111111
启动顺序建议先 master,再 replica:
redis-server /myredis/redis6379.conf
redis-server /myredis/redis6380.conf
redis-server /myredis/redis6381.conf
6. 验证复制关系
进入 master:
redis-cli -p 6379 --pass 111111
查看复制信息:
info replication
master 上应看到:
role:master
connected_slaves:2
replica 上应看到:
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
虽然新术语叫 replica,INFO replication 输出里仍可能看到 slave 字段,这是 Redis 兼容历史名称的表现。
除了 INFO replication,ROLE 也适合快速判断实例角色。日常排查可以组合使用:ROLE 看角色和复制对象,INFO replication 看 offset、lag、backlog 等详细状态。
下面是三台虚拟机环境中的日志和 INFO replication 示例,所以 IP 地址使用 192.168.10.x。如果按前面的单机多端口实验操作,则 master 地址可以继续使用 127.0.0.1。
# Master 日志:从库发起同步请求,Master 执行 BGSAVE 并将 RDB 数据同步给 Replica
2925:M * Delay next BGSAVE for diskless SYNC
2925:M * Starting BGSAVE for SYNC with target: replicas sockets
2925:M * Background RDB transfer started by pid 2931
2931:C * Fork CoW for RDB: current 2 MB, peak 2 MB, average 2 MB
2925:M # Diskless rdb transfer, done reading from pipe, 1 replicas still up.
2925:M * Background RDB transfer terminated with success
2925:M * Streamed RDB transfer with replica 192.168.10.102:6380 succeeded (socket). Waiting for REPLCONF ACK from slave to enable streaming
2925:M * Synchronization with replica 192.168.10.102:6380 succeeded
2925:M * Replica 192.168.10.103:6381 asks for synchronization
2925:M * Full resync requested by replica 192.168.10.103:6381
2925:M * Delay next BGSAVE for diskless SYNC
2925:M * Starting BGSAVE for SYNC with target: replicas sockets
2925:M * Background RDB transfer started by pid 2942
2942:C * Fork CoW for RDB: current 2 MB, peak 2 MB, average 2 MB
2925:M # Diskless rdb transfer, done reading from pipe, 1 replicas still up.
2925:M * Background RDB transfer terminated with success
2925:M * Streamed RDB transfer with replica 192.168.10.103:6381 succeeded (socket). Waiting for REPLCONF ACK from slave to enable streaming
2925:M * Synchronization with replica 192.168.10.103:6381 succeeded
# Replica 日志:从库连接 Master,并完成全量同步
2096:S * Connecting to MASTER 192.168.10.101:6379
2096:S * MASTER <-> REPLICA sync started
2096:S * Non blocking connect for SYNC fired the event.
2096:S * Master replied to PING, replication can continue...
2096:S * Partial resynchronization not possible (no cached master)
2096:S * Full resync from master: 6f56ae34d5b7f0b90dc28c786b04a0ac142c6818:14
2096:S * MASTER <-> REPLICA sync: receiving streamed RDB from master with EOF to disk
2096:S * MASTER <-> REPLICA sync: Flushing old data
2096:S * MASTER <-> REPLICA sync: Loading DB in memory
2096:S * Loading RDB produced by version 7.0.11
2096:S * RDB age 0 seconds
2096:S * RDB memory usage when created 0.94 Mb
2096:S * Done loading RDB, keys loaded: 3, keys expired: 0.
2096:S * MASTER <-> REPLICA sync: Finished with success
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.10.102,port=6380,state=online,offset=516,lag=0
slave1:ip=192.168.10.103,port=6381,state=online,offset=516,lag=1
master_failover_state:no-failover
master_replid:6f56ae34d5b7f0b90dc28c786b04a0ac142c6818
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:516
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:516
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:192.168.10.101
master_port:6379
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_read_repl_offset:530
slave_repl_offset:530
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:6f56ae34d5b7f0b90dc28c786b04a0ac142c6818
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:530
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:15
repl_backlog_histlen:516
7. 主从读写行为
在 master 上写入:
set k1 v1
在 replica 上读取:
get k1
正常可以读到。
但这不代表读写强一致。主从复制默认异步,master 写入成功后,replica 可能还没有追上。对 read-after-write 一致性敏感的请求,应继续读 master,或者在业务层增加一致性保护。
但默认情况下,replica 不允许写:
set k2 v2
会得到类似错误:
READONLY You can't write against a read only replica.
127.0.0.1:6380> set k2 v2
(error) READONLY You can't write against a read only replica.
127.0.0.1:6380>
8. master 宕机后会自动切换吗
普通主从复制不会自动切换。
如果 master 停掉:
redis-cli -p 6379 --pass 111111 shutdown
replica 仍然是 replica,不会自动升级为 master。它会持续尝试重连 master。
可以通过复制状态验证这一点:
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:192.168.10.101
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_read_repl_offset:853
如果希望 master 故障后自动切换,需要 Sentinel 或 Cluster。它们负责故障检测、选主和客户端拓扑更新,主从复制只提供数据复制基础。
9. 命令方式指定主从
除了写配置文件,也可以在线执行:
redis-cli -p 6380 --pass 111111 replicaof 127.0.0.1 6379
旧写法是:
redis-cli -p 6380 --pass 111111 slaveof 127.0.0.1 6379
命令方式成功后,可以看到当前实例变成 replica:
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:e3c389d13376380bbc7a1ea353d4a5bd5ed0798d
master_replid2:f3b36637a1be19ac8e09b4493080693b67eaa7ef
master_repl_offset:1271
second_repl_offset:1272
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1272
repl_backlog_histlen:0
127.0.0.1:6380> replicaof 127.0.0.1 6379
OK
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_read_repl_offset:1271
slave_repl_offset:1271
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:e3c389d13376380bbc7a1ea353d4a5bd5ed0798d
master_replid2:f3b36637a1be19ac8e09b4493080693b67eaa7ef
master_repl_offset:1271
second_repl_offset:1272
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1272
repl_backlog_histlen:0
配置方式和命令方式的区别:
| 方式 | 特点 |
|---|---|
| 配置文件 | 重启后关系仍然存在,适合稳定部署 |
| 命令方式 | 当前运行期生效,重启后是否还在取决于配置是否被重写 |
生产环境建议使用配置管理或编排系统,不要靠手工命令维持长期拓扑。
10. 薪火相传:链式复制
Redis replica 也可以被其他 replica 复制。比如:
6379(master) -> 6380(replica) -> 6381(replica)
这种结构可以减少 master 直接连接的 replica 数量,但也会增加链路延迟。写入需要沿链路逐级传播,越靠后的 replica 越容易出现 latency amplification。
链式复制还会放大故障影响。上游 replica 出现网络抖动或重启时,下游节点也会受到影响。因此生产环境只有在明确需要降低 master 复制连接压力时才建议使用,并且要监控每一层的复制延迟。
配置示例:
# 6380 复制 6379
redis-cli -p 6380 --pass 111111 replicaof 127.0.0.1 6379
# 6381 复制 6380
redis-cli -p 6381 --pass 111111 replicaof 127.0.0.1 6380
这种模式适合了解原理,生产上要谨慎使用,尤其要关注复制延迟和故障影响范围。
11. 反客为主:手动晋升
如果 master 不可用,可以手动让某个 replica 脱离复制关系:
redis-cli -p 6380 --pass 111111 replicaof no one
旧写法:
redis-cli -p 6380 --pass 111111 slaveof no one
执行后,6380 会变成 master。
手动晋升后,可以看到角色已经变成 master:
127.0.0.1:6381> replicaof no one
OK
127.0.0.1:6381> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:33b0eebeea65c789fba91fd94b1e4987754532a2
master_replid2:f3b36637a1be19ac8e09b4493080693b67eaa7ef
master_repl_offset:1954
second_repl_offset:1955
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1636
repl_backlog_histlen:319
127.0.0.1:6381> keys *
1) "k2"
2) "k1"
3) "k4"
4) "k3"
但手动晋升只是临时处理。要让其他 replica 改为复制新 master,还需要继续调整它们的 REPLICAOF 配置,并同步更新客户端或代理层的连接目标。
旧 master 恢复后也不能直接继续承担读写,需要先确认拓扑和数据方向,避免形成两个可写 master 带来的 split-brain 风险。
12. 复制原理
Redis 复制大致分为两个阶段:
- 全量复制
- 增量复制,也叫部分重同步
12.1 首次连接
replica 启动后连接 master,并发起同步请求。现代 Redis 使用 PSYNC 协议来支持全量复制和部分重同步。
首次连接通常无法部分重同步,因此会走全量复制。
12.2 全量复制
全量复制过程如下:
- replica 连接 master。
- master 后台生成 RDB 快照。
- master 在生成快照期间缓存新的写命令。
- master 把 RDB 文件发送给 replica。
- replica 清空旧数据,加载 RDB。
- master 再把期间缓存的写命令发送给 replica。
- replica 追上 master 后进入正常复制状态。
可以把全量复制理解成下面这条链路:
replica 发起 PSYNC
-> master fork 子进程生成 RDB
-> master 记录快照期间的新增写命令
-> replica 加载 RDB
-> master 发送缓存的写命令
-> replica 进入持续复制状态
全量复制不是没有成本。master 生成 RDB 时需要 fork 子进程,fork 本身会带来瞬时开销;写入量较大时,copy-on-write 可能显著增加内存占用,极端情况下会接近额外占用一份数据页。与此同时,RDB 传输和后续命令回放也会带来网络与 IO 压力。
需要注意:replica 首次全量复制会清理自身旧数据,所以不要把已有重要数据的 Redis 实例随便配置成 replica。
12.3 心跳和持续同步
主从之间会持续保持连接。master 后续收到写命令后,会把写命令发送给 replica。
12.4 部分重同步
如果 replica 短暂断线后重新连接,Redis 会尝试部分重同步。
关键机制有三个:
| 名称 | 说明 |
|---|---|
| replication backlog | master 维护的一段环形复制缓冲区 |
| offset | master 和 replica 各自记录复制偏移量 |
| replid | master 的复制 ID |
| replid2 | 故障切换后保留的旧复制 ID,用于提升重同步成功率 |
如果 replica 断线期间缺失的数据仍然在 backlog 中,master 只需要把缺失部分补给 replica。否则只能重新全量复制。
可以通过配置调整 backlog 大小:
repl-backlog-size 64mb
backlog 是环形 buffer,不是普通队列。新的复制数据会不断写入,空间不足时会覆盖更早的数据。
backlog 太小,replica 稍微断线久一点就容易触发全量复制;backlog 太大,会占用更多内存。生产环境通常按写入速率和可接受断线窗口估算,例如:
repl-backlog-size >= 峰值写入字节率 * 期望容忍的断线秒数
网络频繁抖动时,replica 可能反复错过 backlog 窗口,从而触发多次 full resync。这类问题表面看是同步慢,本质上通常是网络稳定性、backlog 容量和写入流量共同作用。
13. 复制延迟问题
主从复制默认是异步复制。master 写成功后,不需要等待所有 replica 都确认完成。
这会带来复制延迟:
- master 写入成功,但 replica 还没同步到。
- 客户端立刻去 replica 读取,可能读到旧值。
- master 宕机时,尚未同步到 replica 的数据可能丢失。
可以用下面命令观察:
info replication
重点看 master 和 replica 的 offset 差距。
如果业务不能接受读旧数据,就不要把强一致读请求打到 replica。高写入场景下还要持续监控 master_repl_offset、slave_repl_offset 或 replica_repl_offset,以及 slave0、slave1 字段里的 lag。
14. 防止 master 在 replica 不足时继续写
Redis 提供了两个配置,可以在 replica 不足时让 master 拒绝写入:
min-replicas-to-write 1
min-replicas-max-lag 10
含义是:至少要有 1 个 replica 延迟不超过 10 秒,否则 master 拒绝写命令。
这不是强一致复制,但可以降低“master 孤岛写入”带来的数据风险。
更准确地说,它是一种弱一致性保护机制。它只能限制 master 在 replica 数量或延迟不满足条件时继续写入,并不保证每次写入都已经被所有 replica 应用,也不能完全消除故障窗口内的数据丢失风险。
15. 常见问题
15.1 replica 为什么连不上 master
优先检查:
ping master-ip
telnet master-ip 6379
redis-cli -h master-ip -p 6379 --pass 111111 ping
再检查:
- bind 是否监听正确地址
- protected-mode 是否阻止远程连接
- 防火墙或安全组是否放行端口
- requirepass 和 masterauth 是否一致
- master 日志是否有认证失败
15.2 为什么 replica 不能写
默认配置:
replica-read-only yes
这正是推荐行为。否则 replica 上写入的数据不会同步回 master,还可能在下一次全量复制时被覆盖。
15.3 master 重启后复制关系还在吗
如果复制关系写在配置文件里,重启后还在。如果只是在线执行 REPLICAOF,需要确认是否写回配置。
master 重启后是否还能让 replica 部分重同步,取决于复制元数据是否连续,以及 backlog 是否覆盖 replica 缺失的 offset。条件满足时可能继续部分重同步;不满足时会退回全量复制。
15.4 replica 重启后会从头复制吗
不一定。能否部分重同步,取决于 replica 保存的复制 ID、offset,以及 master backlog 中是否还保留缺失数据。如果条件不满足,就会全量复制。
16. 本文小结
主从复制是 Redis 高可用体系的基础,但它本身不等于高可用。
核心结论如下:
- 新版本建议使用 REPLICAOF,旧资料中的 SLAVEOF 需要知道但不优先使用。
- master 负责写,replica 默认只读。
- 普通主从下 master 宕机,replica 不会自动升级。
- 首次同步通常是全量复制,后续尽量走部分重同步。
- replication backlog 是部分重同步的关键,大小要结合写入速率和断线窗口评估。
- 主从复制默认异步,存在复制延迟、read-after-write 不一致和数据丢失窗口。
- 如果要无人值守故障转移,需要继续引入 Sentinel 或 Cluster。
下一篇继续看 Redis Sentinel 哨兵,它就是为了解决普通主从“master 挂了没人自动接班”的问题。