Redis 持久化 RDB AOF 选型(redis-persistence-choose)
持久化选型只需要回答一个问题:这个实例丢了数据,最坏能接受多少? 答案决定了配置:0 → AOF always;1 秒 → 混合持久化 + everysec;15 分钟 → 只开 RDB;全丢也可以 → 关掉,前提是它不是主库。
这个技能做三件事:算丢数窗口 → 评 fork 风险 → 扫高危配置组合。
硬边界(先看这条)
| 能做 | 不能做 | |---|---| | 按「允许丢多少」推荐持久化组合 | ❌ 替代真实的恢复演练(备份能不能起来只有演练能证明) | | 测算丢数窗口与 fork/COW 风险 | ❌ 预测具体 fork 耗时(依赖机型、内存、写入速率) | | 扫出高危配置组合(如 fsync=no、重写被关) | ❌ 直接修改线上实例的 redis.conf | | 给出 INFO persistence 的监控字段 | ❌ 承担数据丢失的责任 |
以下情况必须停下来,先做人工确认:
- 主节点未开持久化 + 配置了自动重启 → 随时可能清空全部从节点
aof_last_write_status不是ok→ 磁盘或 IO 已有故障- 准备把实例的持久化从「有」改成「无」→ 必须先把现有备份复制走,再改配置
一、什么时候用这个技能
| 用户这样说 | 就用 | |---|---| | "Redis 该用 RDB 还是 AOF" | ✅ | | "持久化怎么配最少丢数据" | ✅ | | "everysec 到底会丢多少" | ✅ | | "混合持久化要不要开" | ✅ | | "BGSAVE 失败为什么还能写入" | ✅ | | "主库崩了为什么从库数据也没了" | ✅ | | "fork 导致延迟抖动怎么办" | ✅ | | "帮我在线上直接改 redis.conf" | ❌ 走变更流程 |
二、完整工作流
第 1 步 读规则文件 rules/persistence_choice.md
第 2 步 明确回答:这个实例最坏能接受丢多少数据?
第 3 步 导出配置与状态
redis-cli CONFIG GET '*' > redis.conf.txt
redis-cli INFO persistence >> redis.conf.txt
第 4 步 跑体检脚本
python scripts/redis_persistence_check.py --file redis.conf.txt \
--role master --maxmemory-gb 16
第 5 步 按脚本的「丢数窗口 + fork 风险 + 配置检查」三段结论调整
第 6 步 做恢复演练:拿备份文件另起实例,确认能起来、数据条数对得上
第 7 步 补监控告警:rdb_last_bgsave_status / aof_last_write_status / 距上次快照间隔
三、丢数窗口对照表(选型的唯一依据)
| 模式 | 配置 | 丢失窗口 | 恢复速度 |
|---|---|---|---|
| RDB | save 900 1 | 最后一次快照之后的全部写入(可达 15 分钟) | 快 |
| AOF always | appendfsync always | 理论 0,实际最多丢一个事件循环 | 慢 |
| AOF everysec | appendfsync everysec(默认) | 约 1 秒(fsync 落后时 2 秒) | 慢 |
| AOF no | appendfsync no | 由 OS 决定,通常 30 秒 | 慢 |
| 混合持久化 | appendonly yes + aof-use-rdb-preamble yes | 同所选 fsync 档位 | 快(主体 RDB + 增量命令) |
| 全关 | save "" + appendonly no | 进程退出即全丢 | 无 |
选型速查:
| 场景 | 推荐 |
|---|---|
| 纯缓存,丢了最多造成缓存击穿 | 关持久化(但主节点不要关,见 §6)或 save 3600 1 |
| 缓存要保预热 | 混合持久化 + everysec |
| 分布式锁 / 幂等键 / 限流计数 | 混合持久化 + everysec + 至少一个副本 |
| 轻量可靠存储(任务队列、去重集合) | 混合持久化 + always + 副本持久化 + 定期离线备份 |
| >16GB 且要求快速恢复 | RDB 为主 + AOF 混合 |
四、fork 与延迟抖动
BGSAVE / BGREWRITEAOF 都要 fork:
| 关注点 | 口径 |
|---|---|
| fork 耗时 | 通常 10-100ms/GB,实例越大约慢;看 latest_fork_usec |
| COW 峰值 | 理论可接近 2 倍内存 |
| fork 失败 | vm.overcommit_memory=0 且内存吃紧时 fork 失败,持久化静默停止 |
| 抖动加剧 | 开启 THP 会显著放大 fork 与 COW 抖动 |
系统层必配:
sysctl -w vm.overcommit_memory=1
echo never > /sys/kernel/mm/transparent_hugepage/enabled
五、三个必须做的取舍
no-appendfsync-on-rewrite:yes延迟更稳但重写期间会丢数据;no(默认)更安全但可能延迟尖峰。要求不丢就选no。stop-writes-on-bgsave-error:默认yes(快照失败拒绝写入)。改成no必须配套监控rdb_last_bgsave_status,否则备份可能长期静默失败而你毫不知情。auto-aof-rewrite-percentage:设为0等于关闭自动重写,AOF 会一直膨胀。
六、主从场景的经典事故
链路:主节点没开持久化 + 自动重启 → 主节点重启后内存为空 → 从节点向主节点同步 → 从节点数据被清空。
四条规避:主节点至少开 AOF;关闭主节点自动重启;副本也要开持久化(否则重启动全量同步打满主库带宽);定期把 RDB 复制到对象存储。
七、脚本用法
redis-cli CONFIG GET '*' > redis.txt
redis-cli INFO persistence >> redis.txt
python scripts/redis_persistence_check.py --file redis.txt --role master --maxmemory-gb 16
| 参数 | 说明 |
|---|---|
| --file | redis.conf 文本,或 CONFIG GET / INFO persistence 的输出 |
| --role | master / slave / replica,用于角色相关的风险判定 |
| --maxmemory-gb | 实例内存(GB),用于 fork 风险评估 |
| --now | 当前时间戳,配合 INFO 的 rdb_last_save_time 算距上次快照多久 |
| --json / --out | JSON 输出 / 指定输出文件 |
| --selftest | 跑内置自检 |
脚本输出四段:丢数窗口(含 no-appendfsync-on-rewrite 打开后的窗口放大提示)、fork 风险(按内存与快照间隔分级)、配置检查(严重/警示/提示/好)、健康字段(rdb_last_bgsave_status 不为 ok 时直接报严重)。
静态配置检查不等于实际可靠性。唯一能证明备份可用的是恢复演练。
八、人工确认清单(这步不能省)
- [ ] 业务侧确认过「最坏能丢多少」吗
- [ ]
INFO persistence三个 status 是否全为ok - [ ]
rdb_last_save_time距今不超过配置的 save 间隔 - [ ]
vm.overcommit_memory=1且 THP 已关闭 - [ ]
stop-writes-on-bgsave-error=no的实例有对应的告警吗 - [ ] 主从都开了持久化吗;主节点有没有配自动重启
- [ ] 做过一次真实恢复演练吗(起新实例、验证数据条数)
九、最常见的坑
坑一:开了 RDB 就以为万无一失。
save 900 1 意味着最坏丢 15 分钟的全部写入。对金额、库存、幂等键来说完全不可接受。
坑二:以为 everysec 只丢 1 秒,从不验证。 fsync 落后时实际可能丢 2 秒。而且这个结论建立在磁盘 IO 正常的前提上——IO 打满时更长。
坑三:为了不影响业务把 stop-writes-on-bgsave-error 改成 no。
磁盘满了、权限错了,BGSAVE 一直失败,写入却照常进行。直到重启才发现一个月没备份过。改 no 必须配套告警。
坑四:主节点无持久化 + systemd 自动重启。 主库崩溃重启后是空实例,从库老老实实同步,把自己清空。这是最经典的 Redis 数据全损事故链路。
坑五:auto-aof-rewrite-percentage 0。
写 0 的人是想要"禁止自动重写以避免抖动",结果是 AOF 无限膨胀直到磁盘写满。
坑六:always + no-appendfsync-on-rewrite yes。
自相矛盾:重写期间 fsync 暂停,此时已经不是 always 语义了,但你以为自己是最高安全档。
坑七:忽略 fork 抖动。
32GB 实例配 save 60 10000,写入密集时每分钟 fork 一次,每分钟抖一次。大内存实例应该放宽 RDB 间隔,改由 AOF 承担时效性。
坑八:只备份不演练。 备份文件的唯一意义是能恢复出来。定期把 RDB 拉到隔离环境起实例验证——这一步没人做,但它是唯一能证明备份可用的方法。
十、边界与免责
- 本技能给出的是配置层面的静态体检与选型建议,不构成对实际数据可靠性的承诺
- fork 耗时、COW 峰值受机型、内核版本、内存大小与写入模式共同影响,以
latest_fork_usec实测为准 - 生产 Redis 的配置变更属于高风险操作,必须走变更评审,先在从节点或非关键实例验证
- Redis 7 的 multi-part AOF 目录结构与旧版本差异较大,操作前确认版本
十一、许可
本技能由 刘天宝|墨燊 原创,采用 Apache License 2.0。
rules/persistence_choice.md 中的丢失窗口与阈值基于 Redis 官方行为与工程实践整理,不同版本(尤其是 Redis 7 的 multi-part AOF)存在差异,请以实际版本官方文档为准。
Scan to join WeChat group