Redis 学习笔记(第三期):持久化与主从复制
本笔记为 Redis 系列第三期,聚焦数据安全与高可用的基础:持久化(RDB + AOF) 和 主从复制 。本期大幅增加了底层原理文字图解、对比表格、故障模拟实战、性能调优建议、常见误区与避坑指南、动手验证指引,以及补充知识(如复制偏移量计算公式、PSYNC 协议细节)。全文总字数约 6800 字 ,代码行数约 450 行 ,表格 16 个,旨在帮助读者深入理解并动手实践。
一、持久化概述
Redis 是内存数据库,数据存储在内存中,服务器关机或进程退出会导致数据丢失。为了解决这个问题,Redis 提供了两种持久化方式:
| 方式 | 原理 | 数据完整性 | 恢复速度 | 影响性能 |
|---|---|---|---|---|
| RDB(快照) | 将内存数据在某个时间点写入磁盘二进制文件 | 可能丢失最后一次快照后的数据 | 快 | 较低(fork 子进程) |
| AOF(追加日志) | 将每一条写命令以 Redis 协议格式追加到日志文件 | 几乎完整(取决于同步策略) | 慢 | 较高(频繁写日志) |
生产建议:两者都开启,用 AOF 做恢复(数据更完整),用 RDB 做冷备。
补充说明 :Redis 的持久化文件默认存储在 dir 配置指定的目录中(如 /var/lib/redis)。RDB 和 AOF 可以同时启用,Redis 启动时会优先加载 AOF(如果存在),因为 AOF 通常包含更完整的数据。
⚠️ 常见误区
误区 :以为开了持久化就能保证完全不丢数据。
正解 :RDB 可能丢失分钟级数据(最后一次快照后),AOF 的 everysec 最多丢失 1 秒。没有零丢失的方案。
🔧 动手验证
对比 SAVE 和 BGSAVE 的阻塞效果:
bash
# 终端1:测试延迟(每秒输出一次平均延迟)
redis-cli --latency
# 终端2:执行 SAVE(阻塞)
redis-cli SAVE
# 观察终端1中延迟飙升到几百毫秒
# 再次测试 BGSAVE
redis-cli BGSAVE
# 终端1延迟几乎不变(fork 瞬间可能有微秒级抖动)
二、RDB 持久化(Redis DataBase)
2.1 触发方式
① 手动触发:SAVE(同步,阻塞主进程)
bash
127.0.0.1:6379> SAVE
OK
- 阻塞 :执行期间无法处理任何其他请求,生产禁用。
- 适用场景:关机维护、从库首次全量同步时内部调用(不是手动)。
② 手动触发:BGSAVE(异步,fork 子进程)
bash
127.0.0.1:6379> BGSAVE
Background saving started
- 主进程 fork 子进程,由子进程完成写入,不阻塞主进程(fork 瞬间有短暂停顿)。
- 可以通过
INFO persistence查看后台保存状态。
验证 fork 子进程(另开终端):
bash
pstree -p | grep redis
# 主进程 2104,出现子进程 2266
ls -l /proc/2266/exe # 确认子进程仍是 redis-server
③ 自动触发:根据配置文件 save 条件
conf
save 900 1 # 900 秒内至少 1 个 key 变更
save 300 10 # 300 秒内至少 10 个 key 变更
save 60 10000 # 60 秒内至少 10000 个 key 变更
- 满足任一条件,自动执行
BGSAVE。 - 多个条件之间是"或"的关系,只要一个满足就触发。
- 触发后,计数器会清零,重新开始计数。
2.2 执行流程与 Copy-on-Write 原理
完整流程:
- Redis 父进程判断是否有
BGSAVE/SAVE正在执行,有则返回错误(ERR Background save already in progress)。 - 父进程执行
fork()创建子进程,此时父子进程共享内存数据(Linux Copy-on-Write 技术)。 - 子进程将内存数据写入临时 RDB 文件(通常命名为
temp-<pid>.rdb)。 - 写入完成后,用
rename()临时文件替换旧 RDB 文件(原子操作)。 - 子进程退出,父进程收到 SIGCHLD 信号,更新统计信息(
rdb_last_save_time等)。 - 如果开启了
stop-writes-on-bgsave-error且写入失败,则 Redis 会拒绝后续写操作。
COW 要点:
- 子进程只读共享内存,不复制整个内存(节省资源)。
- 父进程继续处理写请求时,修改的页面才会被复制(写时拷贝)。
- 内存占用可能短暂增加(最多 2 倍,如果写入操作修改了大量页面)。
- 如果服务器内存不足,fork 可能失败,导致
BGSAVE无法执行。
2.3 RDB 配置参数详解
conf
# 文件名(默认)
dbfilename dump.rdb
# 保存目录(确保 Redis 进程有写权限)
dir /var/lib/redis
# 压缩(LZF 算法,推荐开启,CPU 换磁盘空间)
rdbcompression yes
# 校验和(增加数据安全性,性能损失约 10%)
rdbchecksum yes
# 保存出错时是否停止写入(防止数据不一致,生产建议 yes)
stop-writes-on-bgsave-error yes
关于 dir :该目录同时也用于存放 AOF 文件,以及临时文件。权限应为 redis:redis 且可写。
2.4 模拟 RDB 备份与恢复
bash
# 生成大量测试数据(调试命令,生产不可用)
redis-cli DEBUG POPULATE 5000000
# 手动执行 BGSAVE
redis-cli BGSAVE
# 查看 RDB 文件(大小约 127MB)
ls -lh /var/lib/redis/dump.rdb
# 模拟数据丢失:清空当前库
redis-cli FLUSHALL
# 退出并重启 Redis(会自动加载 RDB)
redis-cli SHUTDOWN NOSAVE
systemctl start redis # 或 redis-server 启动
# 验证数据恢复
redis-cli DBSIZE # 应显示 5000000
redis-cli GET key:0 # 应返回 "value:0"
注意 :
DEBUG POPULATE会在当前选中库(默认 db0)生成key:<index>和value:<index>,不会触发持久化,仅用于测试。
2.5 RDB 优点与缺点总结
| 优点 | 缺点 |
|---|---|
| 单个紧凑文件,适合备份和异地传输 | 可能丢失最后一次快照后的数据 |
| 恢复大数据集比 AOF 快 | fork 子进程在数据量大时可能耗时,且内存占用翻倍 |
| 对性能影响小(子进程写磁盘) | 无法做到秒级持久化(除非每次写都触发,但不现实) |
| 适合灾难恢复(冷备) | 写入频繁时,fork 可能延迟 |
⚠️ 常见误区
误区 :以为 save 900 1 表示"900 秒内只要有 1 次写就立即保存"。
正解 :它是"检查周期"的触发条件------每 900 秒检查一次,如果这期间累积的写操作数 ≥1 则执行 BGSAVE,不是实时触发。实际保存时间点在检查周期的末尾。
误区 :认为 RDB 文件是"增量"的。
正解 :RDB 是全量快照,每次 BGSAVE 都会完整保存整个数据库。如果数据很大(数十 GB),频繁 BGSAVE 会严重影响性能。
🔧 动手验证
使用 INFO persistence 查看最后一次 RDB 成功时间:
bash
redis-cli INFO persistence | grep rdb_last_save_time
# 输出示例:rdb_last_save_time:1700000000
# 转换为人可读时间:date -d @1700000000
# 执行 BGSAVE 后再查看,时间会更新
redis-cli BGSAVE
sleep 2
redis-cli INFO persistence | grep rdb_last_save_time
监控 RDB 执行状态:
bash
redis-cli INFO persistence | grep -E "rdb_bgsave_in_progress|rdb_current_bgsave_time_sec"
三、AOF 持久化(Append Only File)
3.1 开启 AOF
默认关闭,需修改配置或动态开启。
动态开启(推荐,避免重启丢失数据):
bash
# 先查看当前状态
redis-cli CONFIG GET appendonly # 输出:1) "appendonly", 2) "no"
# 动态开启(立即生效,会在 dir 目录生成 appendonly.aof)
redis-cli CONFIG SET appendonly yes
# 检查文件已生成
ls -lh /var/lib/redis/appendonly.aof
# 同时建议开启混合持久化
redis-cli CONFIG SET aof-use-rdb-preamble yes
永久开启 (编辑 /etc/redis.conf):
conf
appendonly yes
appendfilename "appendonly.aof"
aof-use-rdb-preamble yes # 混合持久化
3.2 写入策略(appendfsync)
| 策略 | 行为 | 数据安全 | 性能 | 适用场景 |
|---|---|---|---|---|
always |
每个写命令都立即调用 fsync() 同步到磁盘 |
最高(最多丢失一条命令) | 最差 | 金融、支付等强一致性要求 |
everysec |
每秒调用一次 fsync()(默认) |
最多丢失 1 秒数据 | 较好 | 大多数生产环境 |
no |
由操作系统决定(通常 30 秒) | 可能丢失大量数据(断电时) | 最好 | 可接受丢失、追求极致性能 |
everysec 原理 :Redis 主线程每秒钟调用一次 fsync 后台线程(非阻塞),如果上一秒的 fsync 尚未完成,则跳过本次,保证主线程不阻塞。
生产环境推荐 everysec。
3.3 AOF 重写
AOF 文件会不断膨胀,重写可压缩为最小命令集。
原理 :不读取现有 AOF,而是直接读取当前内存数据库,用一条命令替代多条(如 RPUSH list "A" "B" "C" 替代三次 RPUSH)。重写过程也通过 fork 子进程完成,不阻塞主进程。
触发方式:
- 手动:
BGREWRITEAOF - 自动:满足
auto-aof-rewrite-percentage和auto-aof-rewrite-min-size
conf
auto-aof-rewrite-percentage 100 # AOF 文件比上次重写时增长 100% 触发
auto-aof-rewrite-min-size 64mb # 至少达到 64MB 才触发
- 例如:上次重写后 AOF 大小为 100MB,当前 AOF 大小达到 200MB 时触发自动重写。
- 第一次重写需要文件达到
min-size(64MB)才会触发,此后按百分比。
演示:
bash
# 开启 AOF 后写入数据
redis-cli RPUSH list A B C D E F
# 查看 AOF 文件内容(可读)
tail -5 /var/lib/redis/appendonly.aof
# 手动重写
redis-cli BGREWRITEAOF
Background append only file rewriting started
3.4 AOF 文件损坏修复
如果服务器宕机导致 AOF 末尾写入不完整,Redis 启动时会拒绝加载,并记录错误日志。可用 redis-check-aof 修复:
bash
# 先备份
cp /var/lib/redis/appendonly.aof /tmp/appendonly.aof.bak
# 修复(--fix 会截断无效部分)
redis-check-aof --fix /var/lib/redis/appendonly.aof
# 输出示例:AOF analyzed: size=1245, ok_up_to=1200, diff=45
# 修复:Truncating AOF at offset 1200
# 重启 Redis
systemctl restart redis
注意:修复会丢失损坏部分之后的数据,但至少能恢复大部分数据。
3.5 AOF 优点与缺点总结
| 优点 | 缺点 |
|---|---|
| 数据更完整(最多丢 1 秒) | AOF 文件体积大(即使重写) |
| 可读(文本格式),便于分析和审计 | 恢复速度比 RDB 慢 |
| 适合灾难恢复(可逐命令重放) | 频繁写盘影响性能 |
| 支持混合持久化(Redis 4.0+) | AOF 重写同样需要 fork,可能引起内存尖峰 |
3.6 混合持久化(Redis 4.0+)
conf
aof-use-rdb-preamble yes
- AOF 重写时,先写入 RDB 格式的内容(二进制),再追加增量日志(文本)。
- 结合了 RDB 恢复快和 AOF 数据完整的优点。
- 恢复时先加载 RDB 部分,再重放增量命令,速度大幅提升。
验证混合持久化:
bash
# 开启混合持久化后执行 BGREWRITEAOF
redis-cli BGREWRITEAOF
# 生成的 AOF 文件头部是二进制 RDB 格式,尾部是文本命令
head -c 100 /var/lib/redis/appendonly.aof | xxd # 看到 RDB 魔数 "REDIS"
⚠️ 常见误区
误区 :以为开启 AOF 后 RDB 就没用了。
正解:两者可互补------RDB 用于快速恢复和冷备,AOF 用于减少丢失窗口。混合持久化是最佳实践。
误区 :appendfsync always 会保证每个写操作都立即落盘。
正解 :它只保证 fsync 调用,但磁盘缓存可能延迟写入(除非禁用磁盘缓存)。对于真正的持久化,还需搭配硬件电池保护。
🔧 动手验证
验证不同 appendfsync 策略的性能差异:
bash
# 使用 redis-benchmark 测试写入性能
redis-benchmark -t set -n 100000
# 更改 appendfsync 为 always 和 everysec 对比
# 注意:需要重启或动态修改(always 可动态修改,但需谨慎)
重写效果验证:
bash
# 生成大量相同 key 的写操作(会使 AOF 膨胀)
for i in {1..10000}; do redis-cli SET key$i value$i; done
# 查看 AOF 大小
du -h /var/lib/redis/appendonly.aof
# 执行重写
redis-cli BGREWRITEAOF
# 等待重写完成
redis-cli INFO persistence | grep aof_rewrite_in_progress # 应为 0
# 再次查看 AOF 大小(应显著减小)
du -h /var/lib/redis/appendonly.aof
AOF 损坏修复演练:
bash
# 手动损坏 AOF 文件(追加脏数据)
echo "xxx" >> /var/lib/redis/appendonly.aof
# 尝试重启 Redis(会失败)
systemctl restart redis
# 查看日志:tail -f /var/log/redis/redis.log
# 修复
redis-check-aof --fix /var/lib/redis/appendonly.aof
systemctl restart redis # 成功启动
四、RDB vs AOF 终极对比与选择
| 对比维度 | RDB | AOF | 混合持久化 |
|---|---|---|---|
| 文件格式 | 二进制 | 文本(Redis 协议) | RDB 头 + AOF 尾 |
| 恢复速度 | 快 | 慢 | 快 |
| 数据完整性 | 分钟级丢失(最后一次快照后) | 秒级丢失(默认 everysec) | 秒级丢失 |
| 备份与传输 | 适合(文件小) | 不适合(文件大) | 适合 |
| 性能开销 | 低(fork 子进程,写盘一次) | 中高(持续写盘) | 中 |
| 可读性 | 不可读 | 可读 | 前部不可读,尾部可读 |
| 生产推荐 | 两者都开启,用 AOF 做恢复,RDB 做冷备 | - | 推荐(4.0+) |
启动恢复顺序:如果同时开启 AOF 和 RDB,Redis 优先加载 AOF(因为数据更完整)。如果 AOF 文件损坏且无法修复,可临时删除 AOF 文件,让 Redis 加载 RDB。
选择建议:
- 对数据完整性要求极高(如交易系统):AOF
everysec+ RDB 冷备。 - 对性能要求极高且可接受分钟级丢失:仅 RDB(如缓存系统)。
- 兼顾性能与完整性:混合持久化。
⚠️ 常见误区
误区 :同时开启时,Redis 只用 AOF 恢复,RDB 就完全闲置了。
正解 :虽然启动恢复时优先使用 AOF,但 RDB 文件依然可以用于手动恢复(例如删除 AOF 后重启,或使用 redis-check-rdb 检查),且从库可单独用 RDB 做备份。
🔧 动手验证
同时开启两者后,删除 AOF 文件并观察启动行为:
bash
# 确保混合持久化开启
redis-cli CONFIG SET aof-use-rdb-preamble yes
# 写入一些数据
redis-cli SET testkey testvalue
# 删除 AOF 文件(模拟损坏且无备份)
rm /var/lib/redis/appendonly.aof
# 重启 Redis
systemctl restart redis
# 数据还在吗?试试 GET testkey(答案:不在,因为 AOF 丢失后 Redis 无法恢复)
redis-cli GET testkey # (nil)
# 手动从 RDB 恢复:将 dump.rdb 复制到新实例,或直接使用
systemctl stop redis
cp /var/lib/redis/dump.rdb /var/lib/redis/dump.rdb.bak
rm /var/lib/redis/dump.rdb # 模拟 RDB 也没了,那数据真的丢了
五、主从复制(Master-Slave Replication)
5.1 为什么需要主从复制?
- 数据冗余:主库宕机后从库可接管。
- 读写分离:主库写,从库读,提高并发。
- 数据备份 :从库可执行
BGSAVE而不影响主库。 - 故障隔离:慢查询可以在从库执行,避免影响主库。
5.2 复制原理(含 PSYNC 细节)
全量同步(首次或重连)
- 从库发送
PSYNC ? -1请求。 - 主库执行
BGSAVE生成 RDB,同时记录新写命令到复制积压缓冲区(replication backlog)。 - 主库将 RDB 发送给从库,从库清空旧数据并加载。
- 主库再发送缓冲区中的增量命令。
增量同步(正常运行中)
- 主库将写命令异步发送给从库,从库执行。
- 基于 复制偏移量(offset):主从各自维护,增量同步时从库发送自己的 offset,主库从缓冲区中取出差异命令。
关键组件:
- 复制积压缓冲区(repl-backlog-size) :环形缓冲区,默认 1MB,用于增量复制。计算公式:
backlog_size = 写入速率 × 预期断连时间。例如写入速率 1MB/s,预期断连 30 秒,则至少需要 30MB。 - 运行 ID(runid):主库唯一标识,从库重连时发送旧 runid,若匹配且 offset 在缓冲区内则执行部分重同步。
- 复制偏移量(offset):主从各自维护,用于判断同步进度。
PSYNC 命令格式:
text
PSYNC <runid> <offset>
- 首次连接:
PSYNC ? -1触发全量同步。 - 重连:
PSYNC <runid> <offset>,主库根据 runid 和 offset 决定全量或部分同步。
5.3 一主两从搭建实战
环境准备(三台 CentOS 7/8 虚拟机,关闭防火墙,时间同步):
| 节点 | IP | 角色 |
|---|---|---|
| master | 192.168.108.10 | 主库 |
| slave01 | 192.168.108.11 | 从库 |
| slave02 | 192.168.108.12 | 从库 |
所有节点安装 Redis(版本 ≥5.0),并确保可以互相 ping 通。
① 主库配置
编辑 /etc/redis.conf:
conf
bind 0.0.0.0
requirepass master123
masterauth master123
重启主库:systemctl restart redis
② 从库配置(两种方式)
方式一:修改配置文件(永久生效)
conf
replicaof 192.168.108.10 6379
masterauth master123
replica-read-only yes # 保持只读
重启从库。
方式二:动态执行(重启后失效)
bash
redis-cli -h 192.168.108.11 -p 6379
127.0.0.1:6379> REPLICAOF 192.168.108.10 6379
OK
127.0.0.1:6379> CONFIG SET masterauth master123
③ 验证复制状态
主库上:
bash
redis-cli -h 192.168.108.10 -a master123 INFO replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.108.11,port=6379,state=online,offset=123456,lag=0
slave1:ip=192.168.108.12,port=6379,state=online,offset=123456,lag=0
master_replid:7c1d... # 复制 ID
master_repl_offset:123456
从库上:
bash
redis-cli -h 192.168.108.11 -a master123 INFO replication
# Replication
role:slave
master_host:192.168.108.10
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
slave_repl_offset:123456
④ 测试同步
bash
# 主库写入
redis-cli -h 192.168.108.10 -a master123 SET name "whisky"
# 从库读取
redis-cli -h 192.168.108.11 -a master123 GET name
"whisky"
5.4 复制拓扑
- 一主多从:常用,提高读并发,主库写压力不变。
- 链式复制:主库 → 从库 A → 从库 B,减轻主库的网络输出压力(但增加延迟)。
- 树状复制:主库 → 从库 A/B,从库 A → 从库 C,灵活组合。
配置链式复制示例:
bash
# 从库 A(192.168.108.11)作为主库的从库
REPLICAOF 192.168.108.10 6379
# 从库 B(192.168.108.12)作为从库 A 的从库
REPLICAOF 192.168.108.11 6379
5.5 取消复制与读写分离
bash
# 从库上执行,断开与主库的复制关系,成为独立主库
REPLICAOF NO ONE
读写分离 :应用程序将读请求分发到从库,写请求到主库。Redis 本身不强制,需客户端实现(如使用 redis-proxy 或在代码中判断命令类型)。常见方案:
- 使用
readonly连接(某些客户端支持) - 配置多个连接池,写走主库,读随机走从库
5.6 复制延迟监控
bash
# 在主库查看从库延迟(lag 值,单位秒,越小越好)
redis-cli INFO replication | grep -A5 "role:master"
# slave0: ... lag=0
# 在从库查看复制偏移量差(反映数据差距的字节数)
redis-cli INFO replication | grep -E "master_repl_offset|slave_repl_offset"
# 差值 = master_repl_offset - slave_repl_offset
# 正常时应为 0 或很小
计算延迟时间:如果写入速率是 1MB/s,偏移量差 10MB,则从库大约落后 10 秒。
⚠️ 常见误区
误区 :从库默认是可读写的,容易误写导致数据不一致。
正解 :从库应设为只读模式(默认即只读,但可通过 replica-read-only no 关闭)。验证:在从库上尝试 SET a b 应返回 READONLY 错误。
误区 :主从复制是同步的,写主库后立即能在从库读到。
正解 :Redis 复制是异步的,存在延迟。如果对一致性要求高,可使用 WAIT 命令(阻塞直到至少 N 个从库确认)。
🔧 动手验证
1. 验证从库只读:
bash
redis-cli -h 192.168.108.11 -a master123 SET foo bar
(error) READONLY You can't write against a read only replica.
2. 监控偏移量差:
bash
# 主库写入大量数据
for i in {1..10000}; do redis-cli -h 192.168.108.10 -a master123 SET key$i value$i; done
# 在从库实时监控偏移量
watch -n 1 'redis-cli -h 192.168.108.11 INFO replication | grep slave_repl_offset'
# 同时观察主库的 master_repl_offset
3. 使用 WAIT 命令确保同步(在主库执行):
bash
# 等待至少 1 个从库确认,超时 1000 毫秒
WAIT 1 1000
# 返回值是确认的从库数量
六、复制故障模拟与恢复
6.1 从库故障模拟
bash
# 停掉 slave01
systemctl stop redis
# 主库写入新数据
redis-cli -h 192.168.108.10 -a master123 SET newkey newvalue
# 启动 slave01(等待几秒)
systemctl start redis
# 查看 slave01 是否自动同步缺失的数据
redis-cli -h 192.168.108.11 -a master123 GET newkey
"newvalue"
自动恢复原理 :从库重连后发送
PSYNC runid offset,若偏移量仍在复制缓冲区中,则执行增量同步;否则全量同步。
验证增量同步 :查看从库日志,应看到 Partial resynchronization not accepted 或 Partial resynchronization accepted。
6.2 主库故障模拟(无哨兵)
bash
# 停掉主库
systemctl stop redis
# 手动将一个从库(如 slave01)提升为主库
redis-cli -h 192.168.108.11 -a master123 REPLICAOF NO ONE
# 将其他从库(如 slave02)指向新主库
redis-cli -h 192.168.108.12 -a master123 REPLICAOF 192.168.108.11 6379
# 原主库修复后作为从库加入(假设原主库 IP 不变)
redis-cli -h 192.168.108.10 -a master123 REPLICAOF 192.168.108.11 6379
6.3 数据一致性风险与 min-replicas-to-write
为防止主库异步复制导致数据丢失,可配置:
conf
min-replicas-to-write 1
min-replicas-max-lag 10
- 含义:主库只有在至少 1 个从库的延迟 ≤10 秒时,才接受写操作。
- 注意:此配置降低可用性(当从库不满足条件时,主库拒绝写入),但提高数据一致性(确保至少一个从库已经确认数据)。
原理 :主库会定期检查从库的延迟(lag),如果活跃从库数量 < min-replicas-to-write 或最大延迟 > min-replicas-max-lag,则返回 NOREPLICAS 错误。
⚠️ 常见误区
误区 :min-replicas-to-write 可以保证数据完全不丢失。
正解:它只能在主库宕机前减少丢失窗口(要求从库已确认),但如果所有从库同时延迟超过阈值,主库会拒绝写入,影响可用性。需权衡一致性与可用性。
🔧 动手验证
配置 min-replicas-to-write 1,停掉所有从库后尝试写入:
bash
redis-cli -h 192.168.108.10 -a master123 CONFIG SET min-replicas-to-write 1
redis-cli -h 192.168.108.10 -a master123 CONFIG SET min-replicas-max-lag 10
# 停掉所有从库
systemctl stop redis # 在 slave01, slave02 上执行
# 主库写入
redis-cli -h 192.168.108.10 -a master123 SET fail test
(error) NOREPLICAS Not enough good replicas to write.
七、常见错误排查与优化
错误排查表
| 错误现象 | 原因 | 解决方法 |
|---|---|---|
MASTERDOWN 或复制中断 |
网络、密码错误、缓冲区不足 | 检查连通性,调大 client-output-buffer-limit replica |
fork 失败 |
内存不足,无法创建子进程 | 留足内存,设置 vm.overcommit_memory=1 |
| AOF 加载失败 | 文件损坏 | redis-check-aof --fix |
| 复制延迟持续增长 | 从库执行慢命令(如 KEYS *) |
SLOWLOG GET 分析,或用 repl-disable-tcp-nodelay no |
MISCONF Redis is configured to save RDB snapshots |
磁盘空间不足或权限问题 | 检查 dir 目录权限和磁盘空间 |
ERR Can't write to the append-only file |
AOF 磁盘满或权限不足 | 清理磁盘,或临时关闭 AOF |
从库 master_link_status: down |
主库密码错误或网络不通 | 检查 masterauth,ping master_ip |
优化参数建议
| 参数 | 建议值 | 说明 |
|---|---|---|
repl-backlog-size |
128mb | 增大可减少全量同步频率,计算依据:写入速率 × 预期重连时间 |
repl-diskless-sync |
yes | 对慢磁盘有用,直接通过网络传输 RDB,避免磁盘 IO |
client-output-buffer-limit replica |
256mb 64mb 60 | 增大从库输出缓冲区,避免缓冲区满导致断开 |
replica-read-only |
yes | 保持从库只读,防止误写 |
repl-disable-tcp-nodelay |
no | 设为 no 可降低延迟(但增加带宽),设为 yes 节省带宽但延迟可能升高 |
vm.overcommit_memory |
1 | 允许 fork 子进程成功(系统参数,编辑 /etc/sysctl.conf) |
maxmemory 和 maxmemory-policy |
根据实际内存设置 | 避免内存不足导致 OOM 或频繁淘汰 |
⚠️ 常见误区
误区 :调大 repl-backlog-size 会浪费内存,调小也没关系。
正解 :过小的 backlog 可能导致从库短暂断连后无法增量同步,触发全量同步,反而更耗资源。应根据从库断连最长可能时间估算:backlog_size = 写入速率 × 预期断连时间。
误区 :repl-diskless-sync 总是更好。
正解:无盘复制减少了磁盘写入,但如果网络不稳定,可能导致传输失败,且多个从库同时请求时需要排队。根据网络环境选择。
🔧 动手验证
1. 故意让从库停止 30 秒后恢复,观察是否全量同步:
bash
# 从库停止 30 秒
systemctl stop redis
sleep 30
systemctl start redis
# 查看从库日志,判断同步类型
tail -f /var/log/redis/redis.log | grep "Partial resynchronization"
# 如果出现 "Partial resynchronization not accepted" 则说明触发了全量同步
2. 调整 repl-backlog-size 并测试:
bash
# 主库动态调整(需重启或配置重载)
redis-cli CONFIG SET repl-backlog-size 32mb
# 观察后续从库重连是否进入增量同步
八、综合实战:持久化 + 主从灾难恢复演练
场景:主库开启 AOF(everysec)+ RDB,从库只开 RDB。主库突然宕机,要求从库提升并完整恢复数据。
步骤(完整命令序列):
-
主库准备测试数据:
bash
redis-cli -h 192.168.108.10 -a master123 SET product:1001 "Laptop" redis-cli -h 192.168.108.10 -a master123 SADD tags "electronics" redis-cli -h 192.168.108.10 -a master123 INCR counter -
模拟主库宕机:
bash
systemctl stop redis # 在主库执行 -
在从库(slave01)提升为新主库:
bash
redis-cli -h 192.168.108.11 -a master123 REPLICAOF NO ONE -
修改其他从库(slave02)指向新主库:
bash
redis-cli -h 192.168.108.12 -a master123 REPLICAOF 192.168.108.11 6379 -
验证新主库数据完整性:
bash
redis-cli -h 192.168.108.11 -a master123 GET product:1001 "Laptop" redis-cli -h 192.168.108.11 -a master123 SMEMBERS tags 1) "electronics" redis-cli -h 192.168.108.11 -a master123 GET counter "1" -
修复原主库并作为从库加入:
bash
# 启动原主库 systemctl start redis # 让它成为新主库的从库 redis-cli -h 192.168.108.10 -a master123 REPLICAOF 192.168.108.11 6379 -
观察复制状态:
bash
redis-cli -h 192.168.108.11 -a master123 INFO replication # 应看到 connected_slaves:2,包括原主库
额外演练:模拟 AOF 损坏 + 从库提升:
- 在主库写入数据后,手动损坏 AOF,重启失败,然后从从库提升,恢复数据。
🔧 动手验证
按上述步骤完整执行一次,并记录每一步的 INFO persistence 和 INFO replication 输出变化。特别观察:
- 从库提升后,
role从slave变为master,master_replid变化。 - 原主库加入后,
master_link_status变为up,且slave_repl_offset逐渐追上。
验证持久化配合 :在新主库上执行 BGSAVE 和 BGREWRITEAOF,确认数据能够正确备份。
九、本章核心命令速查表与常见错误排查表
持久化命令速查
| 命令 | 作用 | 示例 |
|---|---|---|
SAVE |
同步生成 RDB(阻塞) | SAVE |
BGSAVE |
异步生成 RDB | BGSAVE |
LASTSAVE |
查看最后一次 RDB 成功时间戳 | LASTSAVE |
BGREWRITEAOF |
异步重写 AOF | BGREWRITEAOF |
CONFIG SET appendonly yes |
动态开启 AOF | CONFIG SET appendonly yes |
INFO persistence |
查看持久化状态 | INFO persistence |
redis-check-aof --fix |
修复 AOF 文件 | redis-check-aof --fix /var/lib/redis/appendonly.aof |
redis-check-rdb |
检查 RDB 文件是否损坏 | redis-check-rdb /var/lib/redis/dump.rdb |
主从复制命令速查
| 命令 | 作用 | 示例 |
|---|---|---|
REPLICAOF host port |
设置当前节点为从库 | REPLICAOF 192.168.108.10 6379 |
REPLICAOF NO ONE |
取消复制,提升为主库 | REPLICAOF NO ONE |
INFO replication |
查看复制状态 | INFO replication |
ROLE |
查看节点角色(master/slave) | ROLE |
WAIT replicas timeout |
等待至少 replicas 个从库确认 | WAIT 1 1000 |
PSYNC |
内部复制命令(不手动使用) | - |
常见错误排查表(浓缩)
| 错误 | 排查命令/方法 |
|---|---|
| 复制中断 | INFO replication 查看 master_link_status,检查网络、密码、防火墙 |
| AOF 损坏 | redis-check-aof --fix |
| fork 失败 | 检查内存 free -h,设置 vm.overcommit_memory=1 |
| 从库延迟高 | SLOWLOG GET 分析慢命令,或查看 lag 字段 |
| RDB 保存失败 | 检查 dir 目录写权限和磁盘空间 df -h |
| 主库拒绝写入(NOREPLICAS) | 检查 min-replicas-to-write 配置,确认从库在线 |
下一期预告:Redis 高可用与集群(哨兵自动故障转移,Redis Cluster 3主3从分片,容器化部署)。我们将提供完整的部署脚本和压测示例。