Redis 7 持久化:多部分 AOF、fsync 与恢复演练
写请求返回成功,数据是否已经耐受断电?重启时同时存在 RDB 和 AOF,Redis 会选哪个?本文以 Redis Open Source 7.2 为具体配置基线,解释 Redis 7 引入的多部分 AOF。以下命令是隔离 Linux 环境中的演练方案,本次未执行 Redis,不把示意当作实测输出。
RDB 与 AOF:保存状态和重放变化
RDB 保存某个时间点的数据集,适合独立备份;两次快照之间的写入可能丢失。AOF 记录用于重建状态的写操作,但其持久性仍取决于刷盘策略。复制解决副本同步,不能替代历史备份:误删也可能传播。
Redis 7 把 AOF 拆成最多一个 base、一个或多个有序 incr,以及决定有效文件集合的 manifest。base 可以是 RDB 格式;默认 aof-use-rdb-preamble yes,因此 .base.rdb 并不等同于独立的 dump.rdb。目录示意如下,编号只是示例:
appendonlydir/
appendonly.aof.manifest
appendonly.aof.2.base.rdb
appendonly.aof.2.incr.aof
appendonly.aof.3.incr.aof恢复按 manifest 加载 base,再重放有效增量;不能只取最大编号文件。成功重写后,旧文件转为 history 并可被清理,不能把它们当成永久历史档案。文件角色见官方 Redis 7.2 aof.c。
fsync:write 成功不等于已经落盘
写入 AOF 通常先经过操作系统页缓存;fsync 才要求同步到存储。三种策略比较如下,最终可靠性仍依赖文件系统和设备正确兑现同步语义。
| appendfsync | 同步方式 | 故障与延迟边界 |
|---|---|---|
| always | 追加后同步,可将同批请求合并提交 | 回复前等待同步,磁盘延迟进入请求路径 |
| everysec | 后台约每秒同步 | 通常以约一秒写入作为丢失窗口,不能理解为任意故障下的硬上限 |
| no | 不主动 fsync,交给操作系统 | 刷新时机由系统决定,不能承诺一秒窗口 |
everysec 不是“每秒才写一次 AOF”,写文件与同步文件是两件事。no-appendfsync-on-rewrite yes 会在后台保存期间跳过主进程 AOF 同步,以持久性换延迟;此时不能继续套用 everysec 的通常窗口。以下是演练配置片段,dir 必须指向事先创建的专用空目录:
bind 127.0.0.1
port 6397
dir /tmp/redis-persistence-drill
appendonly yes
appenddirname appendonlydir
appendfsync everysec
no-appendfsync-on-rewrite no
aof-use-rdb-preamble yes
save 60 1000这些开关及默认值以官方 7.2 redis.conf 为准。进程被杀与整机断电也不同:前者未必丢掉操作系统缓存,不能用一次进程重启证明断电安全。
rewrite:重建当前状态,再切换清单
连续递增一百次,恢复只需要最终计数值。rewrite 从内存状态生成紧凑 base,不是逐行压缩旧日志,也不是额外的一次 fsync。
切换前,旧 base 与旧增量加上新 incr 仍能构成恢复链;成功后,新 base 与新 incr 成为有效集合。失败不能贸然删除旧链。Redis 7 避免了旧版重写末尾的大量缓冲合并,但 fork、写时复制内存、磁盘空间和 I/O 竞争仍有成本。
触发 BGREWRITEAOF 后,要查看 INFO persistence 的 aof_rewrite_in_progress、aof_rewrite_scheduled 和 aof_last_bgrewrite_status;命令返回不等于文件已经切换完成。机制见官方 BGREWRITEAOF。
恢复优先级:先确认实际启用的来源
正常启动同时启用 RDB 和 AOF 时,Redis 优先采用 AOF,而不是按两个文件的修改时间挑选。AOF 内的 RDB base 属于 AOF 恢复链。AOF 损坏导致启动失败时,不应假设会自动退回独立 RDB。
因此,只恢复 dump.rdb 却保留 appendonly yes,可能并未加载预期数据。RDB 恢复应在独立目录明确关闭 AOF;多部分 AOF 恢复应保留完整目录并启用 AOF。已有 RDB 实例切换 AOF,应先在线启用、等待初次重写成功,再保存配置,不能只改配置后立即重启。恢复与迁移规则见官方 持久化说明。
备份与重启:让文件集合保持一致
RDB 可复制已完成的快照,但应先确认目标快照生成成功。多部分 AOF 需要一起备份 manifest 和其引用的文件;rewrite 中途复制可能拿到不匹配的清单与文件。
在线备份前记录 auto-aof-rewrite-percentage 原值,设为 0,禁止人工重写,等待正在执行及排队的重写结束。然后复制整个 appenddirname,完成后恢复原值。若期间可能重启,要考虑将临时配置持久化,并在结束后同样持久化恢复;任何失败路径也应恢复开关。持续写入时备份末尾可能截断,备份的恢复时点不能凭复制结束时间推定。
更容易理解的首次演练可以暂停写入并正常停机后复制完整目录。以下均仅对独立实例执行;先用前面的配置启动,再运行:
redis-cli -p 6397 SET drill:counter 41
redis-cli -p 6397 INCR drill:counter
redis-cli -p 6397 SET drill:lease alive EX 600
redis-cli -p 6397 BGREWRITEAOF
redis-cli -p 6397 INFO persistence
# 等待重写结束,且最后一次状态为 ok;记录 GET、PTTL、DBSIZE
redis-cli -p 6397 SHUTDOWN
# 停机后复制整个数据目录至另一个空目录,保留原始备份
# 复制配置,修改 dir 为恢复目录、port 为 6398,再启动副本
redis-cli -p 6398 GET drill:counter
redis-cli -p 6398 PTTL drill:lease
redis-cli -p 6398 DBSIZE预期计数为 42,TTL 随实际经过时间减少,甚至已过期;这些是预期现象,不是本次输出。还应记录启动日志中加载的文件、恢复耗时、关键类型和值;键数相同并不能证明内容相同。正常停机验证恢复流程,故障丢失窗口需另做受控异常终止与存储故障实验。
预期失败:尾部截断与中间损坏要分开
只在备份的第二份副本制造损坏。将最后一个增量截断到命令中间:aof-load-truncated yes 通常可舍弃不完整尾命令;改为 no 则预期拒绝启动。中间非法内容不是尾部容错能解决的,应预期报错。
先用同版本 redis-check-aof /恢复目录/appendonlydir/appendonly.aof.manifest 无修复检查,再评估是否对副本加 --fix;修复可能丢弃错误点之后的数据。manifest 入口的检查实现见官方 7.2 redis-check-aof.c。原始备份必须保留。上述失败样例、本机 Redis 重启、备份与故障实验均未执行;本次只完成资料核对和文档静态检查,详见检查记录。本文无 Java 示例。
继续阅读:Redis 知识梳理、缓存一致性与失败补偿及技术进阶阅读入口。
