这是《Redis 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 Redis 是内存数据库,但可以持久化。RDB 快、小、易备份;AOF 丢失窗口小;混合持久化兼顾两者。本章讲清触发机制、文件格式、恢复流程与生产配置。
16.1 为什么需要持久化
没有持久化时,Redis 重启后数据全部丢失。这对纯缓存可以接受,但对锁任务、队列、计数、排行榜则可能是事故。
两类文件:
RDB: 某一时刻内存快照,二进制
AOF: 写命令追加日志
核心权衡:
| 维度 | RDB | AOF |
|---|---|---|
| 文件大小 | 小 | 大 |
| 恢复速度 | 快 | 慢 |
| 数据丢失窗口 | 取决于触发周期 | 取决于 appendfsync |
| CPU/磁盘影响 | fork+子进程写文件 | 持续追加与重写 |
| 可读性 | 二进制 | 文本命令 |
16.2 RDB
save 触发
save 3600 1
save 300 100
save 60 10000
含义:满足“秒内至少修改次数”条件时触发保存。
手动:
SAVE
BGSAVE
LASTSAVE
SAVE:主线程执行,会阻塞,生产禁用;BGSAVE:fork 子进程生成快照,常用。
RDB 文件
dbfilename dump.rdb
dir /data
rdbcompression yes
rdbchecksum yes
RDB 保存二进制快照,加载时直接重建对象,比重放命令快。
16.3 fork 与写时复制
BGSAVE 流程:
1. 主进程 fork 子进程
2. 子进程遍历内存,写 RDB 临时文件
3. 写完后原子替换旧 RDB
fork 后父子进程共享物理内存页,采用 Copy-On-Write:
子进程看到 fork 瞬间的数据视图
父进程继续处理写命令
被修改的页才复制
风险:
- 实例越大,fork 复制页表越慢;
- 写入越多,COW 复制页越多,内存峰值越高;
- 低
overcommit_memory可能导致 fork 失败; - 容器内存 limit 未预留 COW 可能 OOM。
16.4 AOF
开启:
appendonly yes
appenddirname appendonlydir
appendfilename appendonly.aof
appendfsync everysec
流程:
命令执行成功
-> 追加到 AOF 缓冲
-> 根据策略写入文件
-> fsync 到磁盘
appendfsync 策略
| 策略 | 行为 | 丢失窗口 | 风险 |
|---|---|---|---|
always |
每条命令 fsync | 最小 | 吞吐低、磁盘压力大 |
everysec |
每秒一次 | 最多约 1 秒 | 推荐默认 |
no |
交给 OS | 不可控 | 通常不建议 |
everysec 后台线程执行 fsync。如果上一次 fsync 超过 2 秒仍未完成,Redis 会延迟写操作以避免无限堆积。
16.5 AOF 重写
AOF 会不断增长,重写生成一份只保留最终状态的新 AOF:
旧 AOF:
SET k 1
SET k 2
SET k 3
INCR counter
INCR counter
重写后:
SET k 3
SET counter 2
手动触发:
BGREWRITEAOF
自动触发:
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-rewrite-incremental-fsync yes
percentage=100 表示 AOF 比上次重写后大小增长一倍触发;同时需满足 min-size。
16.6 混合持久化
aof-use-rdb-preamble yes
重写后的 AOF 文件:
[RDB 格式基础数据]
[AOF 增量命令]
优点:
- RDB 部分恢复快;
- AOF 部分保留更少丢失窗口;
- 文件小于纯 AOF。
适合大多数需要持久化的生产实例。注意老版本 Redis 或兼容工具必须支持该格式。
16.7 恢复流程
Redis 启动时:
1. 读取配置
2. 检查 AOF 开启
3. AOF 存在 -> 优先加载 AOF
4. AOF 不存在 -> 加载 RDB
5. 重建内存数据
因此当 AOF 开启且文件存在时,RDB 不会作为最新数据源。
相关配置:
aof-load-truncated yes
aof-timestamp-enabled no
aof-load-truncated 允许加载末尾被截断的 AOF,通常用于断电恢复;仍应告警并尽快补齐数据。
16.8 持久化对性能的影响
RDB
- fork 停顿;
- 子进程 CPU/磁盘 IO;
- COW 内存增长。
AOF
- everysec 的磁盘写入;
- 重写期间磁盘 IO;
- AOF 重写缓冲内存;
- 磁盘慢会拖慢写入。
建议
- RDB/AOF 文件放独立磁盘或高性能盘;
- 控制实例大小,降低 fork 时间;
- 避免多个大实例同时 rewrite;
- 监控 latest_fork_usec、aof_delayed_fsync;
- 关键业务用复制和外部备份兜底。
16.9 备份策略
# 生成 RDB
redis-cli -a $PASS BGSAVE
# 查看完成时间
redis-cli -a $PASS LASTSAVE
# 复制文件
cp /data/dump.rdb /backup/dump-$(date +%F-%H%M).rdb
更安全的方式:
- 在从节点执行
BGSAVE; - 将 RDB/AOF 上传对象存储;
- 保留多版本;
- 定期演练恢复。
恢复演练:
mkdir /data/restore
cp dump-20260825.rdb /data/restore/dump.rdb
redis-server --port 6399 --dir /data/restore --appendonly no
redis-cli -p 6399 DBSIZE
16.10 生产配置模板
缓存实例:
save ""
appendonly no
maxmemory-policy allkeys-lru
允许分钟级丢失:
save 900 1
appendonly no
允许秒级丢失:
appendonly yes
appendfsync everysec
aof-use-rdb-preamble yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 1gb
不能丢:
appendonly yes
appendfsync everysec
aof-use-rdb-preamble yes
save 3600 1 300 100 60 10000
即使如此,异步复制下主故障仍可能丢失最后写入;“绝对不能丢”必须由数据库事务/消息系统/业务确认机制保证。
本章小结
- RDB 是快照,恢复快、文件小,但丢失窗口大;
- AOF 是写日志,丢失窗口小,但文件大、恢复慢;
- BGSAVE/BGREWRITEAOF 都依赖 fork 与 COW;
- 混合持久化是大多数实例的默认选择;
- AOF 存在时优先加载 AOF;
- 备份必须定期演练恢复,不能只备份不验证。
思考题
- AOF
everysec一定最多丢 1 秒吗?哪些故障会导致更多丢失? - 为什么大实例 RDB fork 期间可能出现内存增长?
- 纯缓存实例要不要开启 AOF?从恢复时间、磁盘和业务语义分析。