Redis 持久化踩坑:RDB 与 AOF 数据丢失复盘

很多团队把 Redis 当作纯缓存用,直到一次重启或主从切换后发现数据”凭空消失”,才第一次认真看持久化配置。Redis 持久化(RDB 与 AOF)决定了宕机后数据能恢复多少,配置不当轻则丢几分钟数据,重则整库归零。本文复盘几个真实踩坑,讲清 RDB 与 AOF 的取舍、混合持久化落地,以及把数据丢失窗口压到秒级的线上配置。

一、RDB 与 AOF 到底存了什么

RDB 是某一时刻的内存快照(snapshot),由 fork 子进程把数据压缩成二进制 dump.rdb;AOF 则是把每次写命令追加到 appendonly.aof 日志,重启时重放日志重建数据。两者目标相同——把内存数据落到磁盘,但代价与数据安全性完全不同。理解它们之间的差异,是避免持久化踩坑的前提。

维度RDBAOF
数据丢失窗口取决于 save 间隔,可能丢数分钟everysec 最多丢 1 秒
恢复速度快(直接载入二进制)慢(需重放命令,文件越大越久)
文件体积小(压缩)大(命令日志,可重写压缩)
对性能影响fork 瞬间有开销追加写入,everysec 温和
可读性二进制不可读文本命令可读,可人工修复

二、RDB 踩坑:快照间隔就是数据丢失窗口

RDB 默认靠 save 规则触发:save 900 1 表示 900 秒内至少 1 次写才触发一次快照。问题就在这”至少”——如果业务写量低,可能半小时才落一次盘;一旦这期间宕机,这半小时的所有写入全部丢失。更隐蔽的坑是 bgsave 失败:当磁盘满、fork 失败或权限异常,Redis 默认 stop-writes-on-bgsave-error yes 会直接拒绝新写入,把”丢数据”变成”写阻塞”,应用大面积报错。另一个常见误用是把 RDB 当唯一持久化手段,却设了极稀疏的 save 规则,等于主动放弃数据安全。

# RDB 快照规则:满足任一条件即触发 bgsave
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes   # bgsave 失败则拒绝写入
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis

三、AOF 踩坑:appendfsync 决定你能丢多少

3.1 appendfsync 三档取舍

AOF 安全性由 appendfsync 决定:always 每次写都 fsync(最安全、最慢),everysec 每秒 fsync 一次(默认,最多丢 1 秒),no 交给操作系统(最快、最容易丢)。很多事故源于把默认 everysec 改成了 no “图性能”,结果一次宕机丢了几十秒到几分钟数据。

3.2 重写期间的性能抖动

另一个坑是 AOF 重写(rewrite):当日志膨胀,Redis 会 fork 子进程重写出精简日志,若重写期间主进程还在接收大量写入、且磁盘 IO 跟不上,会出现 aof_delayed_fsync 激增、命令堆积。还有 aof-load-truncated:AOF 尾部损坏时,默认 yes 会截断续读;若希望发现损坏就报错(更安全审计),需要权衡。

appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec          # 默认,最多丢 1 秒
no-appendfsync-on-rewrite no  # 重写时仍 fsync,牺牲一点性能换安全
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes        # 尾部损坏则截断续读
aof-use-rdb-preamble yes      # 混合持久化:AOF 头部用 RDB 快照

四、混合持久化:RDB 速度 + AOF 安全

Redis 4.0 起支持 aof-use-rdb-preamble yes,让 AOF 重写时先以 RDB 格式保存全量快照,再追加增量 AOF 命令。这样既保留了 RDB 恢复快的优点,又通过后续 AOF 命令把数据丢失窗口压到秒级,是生产环境的推荐姿势。它的代价是 AOF 文件体积略大,但换来的是可控的秒级丢失窗口,性价比极高。但要注意:开启混合持久化后,AOF 文件前半段是二进制 RDB,不能再用肉眼看命令,故障排查得靠 redis-check-aof 工具,不能像纯 AOF 那样直接 tail 日志。

# 查看持久化状态与最近一次 bgsave/aof 信息
redis-cli INFO persistence

# 手动触发 AOF 重写(日志过大时)
redis-cli BGREWRITEAOF

# 修复损坏的 AOF 文件(裁剪到最后一个合法命令)
redis-check-aof --fix appendonly.aof

# 紧急关闭 AOF 重写对主线程的干扰(临时)
redis-cli CONFIG SET auto-aof-rewrite-percentage 0

更多缓存层防护,参见 Redis 缓存三大问题防护Redis 缓存设计入门

五、一次真实数据丢失复盘

5.1 根因拆解

某业务把 Redis 当作”带持久化的主存储”(而非纯缓存),RDB 用默认 save 900 1、AOF 没开。某天夜里一次 OOM kill 重启后,发现最近约 40 分钟的用户会话与计数器全部丢失——因为 RDB 快照是 13 小时前生成的(写入量没达到 save 阈值)。根因有三:① 混淆了”缓存”与”存储”的定位;② 只靠稀疏 RDB,丢失窗口巨大;③ 没有对持久化失败做监控,bgsave 是否成功无人知晓。

5.2 修复动作

修复动作:开启 AOF(everysec)+ 混合持久化、把关键业务迁出 Redis 改落数据库、并在 Prometheus 监控 里对 rdb_last_bgsave_status、aof_last_write_status 设告警。复盘结论:Redis 可以是”强一致存储”的辅助,但绝不能在没有 AOF 的情况下独自承担关键数据。

踩坑现象根因修复
只配稀疏 RDB重启丢数十分钟数据save 阈值未达,长期不落盘开启 AOF everysec
appendfsync 改 no宕机丢几十秒~分钟交由 OS 刷盘不可控改回 everysec
磁盘写满写入被拒、应用报错stop-writes-on-bgsave-error独立盘+预留空间+告警
AOF 文件损坏启动失败尾部截断redis-check-aof –fix

六、线上配置与监控建议

生产环境建议:AOF 开启、appendfsync everysec、混合持久化开启;RDB 保留做冷备与快速迁移;磁盘用独立 SSD 并预留 20% 空间,避免写满触发 stop-writes;主从架构下从库也建议开持久化(故障切换后新主能快速重建)。监控侧至少盯住:rdb_last_bgsave_status(是否 ok)、latest_fork_usec(fork 耗时,过大说明内存大)、aof_current_size / aof_buffer_length(AOF 积压),这些指标都能从 INFO persistence 拿到,配合 Loki 日志收集 做长期留存。

七、上线前自检清单

  • 明确 Redis 定位:缓存还是存储?关键数据务必落库。
  • AOF 开启 + everysec + 混合持久化,别裸用稀疏 RDB。
  • 监控 bgsave/aof 状态,失败即告警,别等重启才暴露。
  • 磁盘预留空间,防止写满触发写阻塞。
  • 主从都开持久化,故障切换后快速重建。
  • 定期演练恢复:从 RDB/AOF 真实还原一次,验证完整性。

Redis 持久化不是”开了就安全”。RDB 与 AOF 各有取舍,混合持久化兼顾速度与数据安全,但真正的防线是监控与定位清晰——别让 Redis 在没开 AOF 的情况下独自扛起关键数据,那才是最贵的踩坑。

上一篇 大模型结构化输出实战:JSON Schema 与提示工程
下一篇 Linux 性能排查实战:top、iostat 与火焰图定位瓶颈