Appearance
主从复制
Redis 主从复制把主库的数据变更同步到一个或多个从库。写请求进主库,主库把变更流推给从库。从库通常用于读扩展、故障切换候选节点和备份源。
Redis 的复制不是强一致的——主库写入成功返回后,从库可能还没来得及收到这条变更。主库突然故障时,最后一段数据可能丢失。复制提供的是"副本"和"恢复基础",不是数据库级别的强一致事务。
一、复制建立的过程
从库连接主库后,根据情况走全量复制或部分复制:
全量复制(从库初次连接,或复制积压缓冲区里缺失了对应数据):
text
从库连接主库 → 主库 fork 生成 RDB → 主库发送 RDB 到从库 → 从库加载 RDB → 从库继续接收 RDB 之后的增量命令全量复制期间,主库的 fork 和 RDB 传输都会消耗资源。如果从库很多且同时发起全量复制(比如所有从库同时重启),主库的压力是叠加的。
部分复制(网络短暂断开后重连,且断开期间的数据还在 backlog 里):从库上报自己当前的 offset,主库从 backlog 里把缺失的那段增量发过去。不会触发全量复制。
相关配置:
conf
# 复制积压缓冲区——主库维护一个环形缓冲区,记录最近的写命令
# 从库重连后如果 offset 还在缓冲区范围内,就只补增量;否则走全量复制
repl-backlog-size 256mb
repl-backlog-ttl 3600
# 全量复制时直接通过 socket 发送 RDB,不额外落盘(对磁盘慢的机器更友好)
repl-diskless-sync yesrepl-backlog-size 的估算思路:写入速率 × 期望容忍的最大断开时间。如果主库每秒写入 10MB,期望 60 秒内的断链都能走部分复制,backlog 至少需要 10MB × 60 = 600MB。设太小频繁全量复制,设太大浪费内存。
二、配置主从
主库 192.168.10.11:
conf
bind 127.0.0.1 192.168.10.11
port 6379
appendonly yes # 主库建议开启持久化,否则重启后内存空、同步给从库也空从库 192.168.10.129:
conf
bind 127.0.0.1 192.168.10.129
port 6379
replicaof 192.168.10.11 6379 # 启动后自动连接主库建立复制
appendonly yes如果主库有密码,从库需要配 masterauth:
conf
masterauth strong_password_here也可以在运行时动态建立或取消复制关系:
bash
# 让当前实例成为从库
redis-cli -h 192.168.10.129 -p 6379 REPLICAOF 192.168.10.11 6379
# 让从库脱离复制,变成独立主库(故障切换时会用到)
redis-cli -h 192.168.10.129 -p 6379 REPLICAOF NO ONE运行时命令适合临时操作,但持久化配置要写进配置文件——不然重启后会恢复到配置文件里定义的拓扑。
三、查看复制状态
主库:
bash
redis-cli INFO replication| 字段 | 含义 |
|---|---|
role:master | 当前角色是主库 |
connected_slaves | 已连接的从库数量 |
master_repl_offset | 主库当前的复制偏移量——每写入一条命令 offset 就增长 |
repl_backlog_active | 复制积压缓冲区是否正在使用 |
从库:
bash
redis-cli -h 192.168.10.129 INFO replication| 字段 | 含义 |
|---|---|
role:slave | 当前角色是从库 |
master_host | 主库地址 |
master_link_status | up 或 down——链路是否正常 |
master_last_io_seconds_ago | 最近一次收到主库数据距今多少秒 |
slave_repl_offset | 从库当前回放到的 offset |
判断延迟的核心方法是对比主从的 offset:
bash
# 主库上
redis-cli -h 192.168.10.11 INFO replication | grep master_repl_offset
# 从库上
redis-cli -h 192.168.10.129 INFO replication | grep slave_repl_offset两者的差值就是当前未同步的字节量。差值持续扩大说明从库追不上主库——可能的原因:网络带宽不够、从库 CPU 或磁盘慢、主库写入量太大、从库正在做 BGSAVE 或 AOF 重写抢了资源。
四、从库只读
从库默认只读:
conf
replica-read-only yes这能防止应用误写从库。故障切换时(Sentinel 或手动),新主库会取消这个限制。
主从链路断开时,从库的行为由 replica-serve-stale-data 控制:
conf
replica-serve-stale-data yes # 断开后继续响应读请求(读到的可能是旧数据)设为 no 则断开后大多数命令直接报错。保留 yes 的好处是读扩展仍然可用(读到的数据只是旧一点而非错误);坏处是应用可能读到过时的数据而不自知。配置类、榜单类场景读旧一点通常能接受;库存、锁状态这类读旧数据可能造成业务错误。
五、读写分离的应用要点
读写分离的典型方式:
text
写请求 → 主库
读请求 → 从库核心问题是复制延迟——刚写到主库的数据,去从库读可能还没到。处理方式分场景:
| 场景 | 适合读从库 | 不适合 |
|---|---|---|
| 首页配置、排行榜、统计 | 延迟几秒不影响展示 | — |
| 备份导出 | 避免主库直接承受备份读取 | — |
| 大量读请求分摊 | 读多写少,能容忍短暂延迟 | — |
| 刚写完马上要读到 | — | 复制延迟导致读到旧值 |
| 库存扣减、余额判断 | — | 一致性要求高 |
| 分布式锁状态 | — | 延迟会破坏互斥判断 |
业务如果要求"读己之写"(刚写入的数据立即能读到),写之后的读请求要发给主库,或者在应用层做短时间的主库读标记。
六、复制安全参数——控制写入的"副本门槛"
主库可以配置:在从库数量不足或延迟过高时拒绝写入。目的是降低主库"孤岛写入"的风险:
conf
min-replicas-to-write 1 # 至少 1 个从库满足条件才允许写
min-replicas-max-lag 10 # 从库延迟超过 10 秒就不算"满足条件"这个配置的取舍很清楚:从库全部异常时,主库会拒绝写入——宁愿短时间写失败,也不让主库在没有副本的情况下持续写入,因为一旦主库在这期间宕机,这些数据就永久丢失了。对数据安全性要求高的核心系统会开这个参数;纯缓存场景通常不开,因为这等于让从库的可用性绑架了主库的可用性。
单次写入后也可以用 WAIT 命令确认副本接收情况:
bash
redis-cli SET order:1001 status:paid
redis-cli WAIT 1 1000 # 等至少 1 个从库确认收到,最多等 1000msWAIT 返回实际确认的从库数量。返回 0 说明超时前没有从库确认——但写入在主库上已经成功了。这不是事务回滚,业务代码要处理"主库写成功但副本未确认"的中间状态。
七、复制异常排查
| 现象 | 排查方向 |
|---|---|
master_link_status:down | 网络不通、主库密码错、防火墙拦截、主库 bind 没绑从库能连到的地址 |
| 反复全量复制 | backlog 太小、网络频繁抖动、从库频繁重启 |
| 从库延迟持续扩大 | 主库写入量超出从库处理能力、网络带宽瓶颈、从库在做 BGSAVE/AOF rewrite |
| 从库连不上主库 | ss -lntp 确认主库监听地址、安全组/防火墙规则、masterauth 密码、protected-mode |
排查步骤:
bash
# 从库侧测试主库端口是否可达
nc -vz 192.168.10.11 6379
# 从库侧测试密码是否正确
redis-cli -h 192.168.10.11 -a 'strong_password_here' PING
# 看主从双方日志
journalctl -u redis-6379 -n 100 --no-pager
tail -n 100 /var/log/redis/redis-6379.log复制异常时把 INFO replication 的完整输出保存下来——它记录了当时的角色、offset、链路状态和从库数量,复盘时不用靠猜。
八、从库做备份
从库常被用来承担备份任务,避免主库执行 BGSAVE 时的 fork 影响主库性能:
bash
redis-cli -h 192.168.10.129 BGSAVE
redis-cli -h 192.168.10.129 INFO persistence | grep rdb_last_bgsave_status但从库备份的前提是从库延迟不大——如果从库已经落后很多,备份出来的 RDB 也落后同样多。备份前检查从库状态:
bash
redis-cli -h 192.168.10.129 INFO replication | grep -E 'master_link_status|master_last_io_seconds_ago'master_link_status 为 up 且 master_last_io_seconds_ago 在几秒以内,备份的数据才算"够新"。备份成功和数据够新是两件事。
九、手工切换的基本步骤
Sentinel 或 Cluster 自动切换之前,手工切换是排查和演练的基础:
bash
# 1. 在选定的从库上提升为主库
redis-cli -h 192.168.10.129 REPLICAOF NO ONE
# 2. 确认新主库角色
redis-cli -h 192.168.10.129 INFO replication | grep role
# 3. 旧主库恢复后,作为新主库的从库加入,避免双主写入
redis-cli -h 192.168.10.11 REPLICAOF 192.168.10.129 6379手工切换最麻烦的不是这几条命令,而是应用连接怎么切到新主库、旧主库残留的写入怎么处理、其他从库要不要改挂新主。切完之后除了 PING,还要做一次写入验证确认新主库确实可写。