Redis 系列(五):持久化——RDB、AOF 与混合持久化
2026/8/10 15:02:15 网站建设 项目流程

核心目标:理解 RDB、AOF、混合持久化三种方式的机制、代价与恢复语义;掌握 fork + COW、fsync 策略、AOF 重写等关键概念;能设计符合业务丢失容忍度的持久化策略,并在文件损坏后完成修复与恢复。

前置知识:完成 Part 1(单线程事件循环——持久化是"阻塞点"的重要来源)与 Part 3(内存结构)。

验证环境:Redis 8.10.0(cygwin 移植版,主实例 6379 用于常规实验,隔离实例 6380 用于断电/损坏等破坏性实验)、redis-py 8.1.0、Python 3.11.6、Windows 11。最后复核日期:2026-08-07。


0. 本篇问题场景:数据"没丢"只是运气

"Redis 是内存数据库,重启就清空"是常见的误读。真实情况是:Redis默认配置下appendonly no)只做 RDB 快照,且快照触发频率可能很低——kill -9或断电后,上一次快照之后写入的所有数据都会消失

先定义本系列反复用到的词:丢失窗口(数据丢失时间窗)——从最后一次"落盘"到故障发生之间的时间。业务能容忍丢失多少秒,直接决定持久化配置:

业务丢失容忍合理的持久化策略
商品缓存(可重建)分钟级RDB 即可,甚至不持久化
购物车/会话秒级AOF everysec
支付流水/状态机零容忍AOF always + 主从 + 备份

本篇用隔离实例把三种持久化的"保存-恢复-损坏-修复"完整演一遍。


1. RDB:内存快照

1.1 机制:fork + Copy-On-Write

RDB 是某时刻全内存数据的二进制快照。触发SAVE(同步)或BGSAVE(后台)时:

  1. 主进程fork()一个子进程;
  2. 子进程把内存中的数据序列化写入dump.rdb
  3. 主进程继续服务客户端。

关键在fork + COW(写时复制)

dump.rdb子进程(fork)主进程dump.rdb子进程(fork)主进程COW:谁先写,谁复制该页内存页fork()(页表复制,共享物理页)继续服务写命令(修改的页被复制)基于"快照时刻"的数据写文件完成后通知主进程

COW 的代价:fork 瞬间复制页表(大实例可能几十~几百毫秒);写命令触发页复制(写入越频繁,复制越多);快照期间内存可能短时上升(被复制的页)。

1.2 触发方式

方式命令/配置阻塞性
手动同步SAVE阻塞主进程直到写完
手动后台BGSAVEfork 瞬间阻塞,之后后台写
自动save 900 1/save 300 10/save 60 10000同 BGSAVE
关闭自动save ""手动控制

1.3 实测:保存、校验、恢复

在隔离实例上完整走一遍(写入 100 个键 → SAVE → 校验 → 重启):

$ redis-cli -p 6380 SAVE OK $ ls -la dump.rdb -rw-r--r-- 1875 dump.rdb ← 二进制快照,1875 字节 $ redis-check-rdb dump.rdb [offset 1875] Checksum OK [offset 1875] \o/ RDB looks OK! \o/ [info] 100 keys read (重启实例) $ redis-cli -p 6380 DBSIZE 100 ← 数据完整恢复 $ redis-cli -p 6380 GET order:42>* Loading RDB produced by version 8.10.0 * RDB age 15 seconds * Done loading RDB, keys loaded: 100, keys expired: 0.

RDB 的定位是"定期快照":恢复快(直接载入二进制)、文件紧凑,但丢失窗口 = 距上次快照的时间。


2. AOF:追加式命令日志

2.1 机制:把"写命令"本身记下来

AOF(Append Only File)记录每条修改数据的命令(RESP 协议格式),重启时重放命令重建数据。相比 RDB,它把丢失窗口压缩到"最后一次 fsync 到故障"之间。

写入链路:

客户端写命令 → 命令执行(内存生效) → 写入 AOF 缓冲 → 按 appendfsync 策略刷入 OS 文件缓存 → 按策略 fsync 落盘

appendfsync三个档位的丢失窗口

策略fsync 时机丢失窗口性能
always每条命令后0(严格说:命令返回前已落盘)最慢,每秒数千~数万次 fsync
everysec每秒一次最多 1 秒生产默认,性能/安全平衡
no交给 OS 决定取决于 OS 刷盘时机(理论最长可达秒级~分钟级)最快

2.2 7.0+ 的多部件 AOF:文件结构变了

Redis 7.0 重构了 AOF:不再是单个appendonly.aof,而是一个appendonlydir/目录,由三部分组成:

appendonlydir/ ├── appendonly.aof.manifest ← 清单:base/incr 文件与顺序 ├── appendonly.aof.1.base.rdb ← base 文件(当前全量;纯 AOF 或混合 RDB 头) └── appendonly.aof.1.incr.aof ← incr 文件(base 之后的增量命令)

实测的 manifest 内容:

file appendonly.aof.1.base.rdb seq 1 type b file appendonly.aof.1.incr.aof seq 1 type i startoffset 0

incr 文件就是 RESP 协议的命令日志(可读):

*1 $5 MULTI *2 $6 SELECT $1 0 *3 $3 ...

AOF 重写BGREWRITEAOF)把当前全量数据写成新的 base 文件,并清空/截断增量——防止 AOF 无限膨胀。重写同样是 fork 子进程 + 后台进行,与 RDB 的 COW 机制一致。

2.3 实测:AOF 保存与恢复

$ redis-cli -p 6380 BGREWRITEAOF Background append only file rewriting started $ ls appendonlydir/ appendonly.aof.2.base.rdb 21875 字节 ← 重写后的全量 appendonly.aof.2.incr.aof 0 字节 appendonly.aof.manifest $ redis-check-aof appendonlydir/appendonly.aof.manifest BASE AOF appendonly.aof.2.base.rdb is valid All AOF files and manifest are valid (重启实例) $ redis-cli -p 6380 DBSIZE 1000 ← 完整恢复 $ redis-cli -p 6380 GET order:99>3. 混合持久化:RDB 头 + AOF 尾

纯 AOF 在启动加载时要重放全部命令,数据量大时启动慢。混合持久化(aof-use-rdb-preamble yes7.x 默认开启)让 AOF 重写后的 base 文件直接用 RDB 格式(快照加载),之后的增量继续用 AOF 追加:

$ head -c 9 appendonlydir/appendonly.aof.2.base.rdb | xxd 00000000: 5245 4449 5330 3031 35 REDIS0015 ← RDB 魔数

redis-check-aof对它的识别:

RDB preamble is OK, proceeding with AOF tail...

加载优先级:只要 AOF 开启,启动时只从 AOF(含混合 base)加载,不读dump.rdb——AOF 永远包含更新的数据。RDB 只在 AOF 关闭时生效。


4. 失败实验:损坏与修复

4.1 AOF 损坏:启动失败 → 修复 → 恢复

向 incr AOF 文件中间插入垃圾字节后启动:

# Bad file format reading the append only file appendonlydir/appendonly.aof.2.incr.aof at offset 0. # make a backup of your AOF file, then use ./redis-check-aof --fix <filename.manifest>. # Alternatively you can set the 'aof-load-corrupt-tail-max-size' configuration option # to 28 and restart the server.

注意细节:base 文件加载成功了Done loading RDB, keys loaded: 1000),失败在损坏的 incr 段——多部件 AOF 让"全量部分无损,增量部分可修"成为可能。修复:

$ redis-check-aof --fix appendonlydir/appendonly.aof.manifest RDB preamble is OK, proceeding with AOF tail... ... AOF appendonly.aof.2.incr.aof format error AOF analyzed: ... ok_up_to=0, diff=28 This will shrink the AOF ... from 28 bytes, with 28 bytes, to 0 bytes Continue? [y/N]: y Successfully truncated AOF appendonly.aof.2.incr.aof All AOF files and manifest are valid

重启后数据完整恢复(1000 键,来自未损坏的 base):

$ redis-cli -p 6380 DBSIZE 1000 $ redis-cli -p 6380 GET product:999>4.2 RDB 损坏:CRC 校验拦截

破坏dump.rdb的 checksum 后:

$ redis-check-rdb dump.rdb --- RDB ERROR DETECTED --- [offset 7375] RDB CRC error [additional info] While doing: check-sum [info] 500 keys read

启动时同样被拦截:

# Wrong RDB checksum expected: (5baeea71929c254a) but got (a451158e6d63dab5). Aborting now. # Internal error in RDB reading offset 0, function at rdb.c:5062 -> RDB CRC error

Redis 拒绝加载损坏的 RDB 并直接退出(默认rdbchecksum yes且不做自动修复)——这比"带病启动"安全:宁可起不来,也不给应用喂一份被篡改的数据。生产修复路径是:用备份 RDB → 或用redis-check-rdb报告损坏程度 → 必要时结合 AOF 恢复。

4.3 "断电"实验:appendfsync no 的意外结果

taskkill /F强制杀掉 AOF(appendfsync no)实例模拟断电,重启后1000 个键全部还在

$ taskkill /F /PID 46440 # 模拟断电 成功: 已终止 PID 为 46440 的进程。 (重启) $ redis-cli -p 6380 DBSIZE 1000

这个结果必须谨慎解读appendfsync no只是"不主动 fsync",数据仍会经OS 文件缓存异步刷盘——Windows 上缓存刷盘非常快,所以"写入后立即 kill"通常已经落盘。它不能证明 no 策略安全:如果 OS 自身崩溃或整机断电,尚未刷盘的缓存照样丢;丢失窗口在理论上依然存在且不可控。这个实验的意义是说明:“实测没丢"≠"策略安全”,丢失窗口是概率与环境的函数。生产仍应选everysec(丢失窗口有界 ≤ 1s)或always(零丢失)。


5. 持久化策略设计

5.1 三种方式对比

维度RDBAOF(everysec)混合(默认)
丢失窗口距上次快照(分钟级)≤ 1 秒≤ 1 秒
恢复速度快(二进制载入)慢(重放命令)快(RDB 头 + 增量)
文件大小紧凑可能膨胀(需重写)紧凑(重写后 RDB 头)
写路径开销fork + COW追加 + 每秒 fsync同 AOF
故障文件修复redis-check-rdb(只报告)redis-check-aof --fix(可截断修复)同 AOF

5.2 生产推荐组合

appendonly yes # 打开 AOF appendfsync everysec # 丢失窗口 ≤ 1s(默认) aof-use-rdb-preamble yes # 混合持久化(默认) save 900 1 300 10 60 10000 # 定期 RDB 作为额外备份点

再加三道保险(后续 Part 10/12 展开):

  1. 主从复制:至少一个从库,主库故障自动切换;
  2. 异地备份:定期把 RDB/快照拷贝到其他存储;
  3. 恢复演练:周期性地"备份 → 恢复到干净实例 → 校验数据"。

6. 版本与环境差异

差异点7.0 之前7.x / 本机 8.10影响
AOF 文件形态单个appendonly.aof多部件目录appendonlydir/(manifest + base + incr)备份/修复命令参数变化;redis-check-aof --fix传 manifest 文件
aof-use-rdb-preamble4.0 引入,5.0 起默认开启默认 yes(混合持久化)base 文件是.rdb格式
AOF 加载失败策略直接拒绝仍拒绝,但可配aof-load-corrupt-tail-max-size容忍尾部损坏高可用场景可有限容忍
rdbchecksum默认 yes默认 yes(CONFIG GET实测)损坏 RDB 启动即 abort(§4.2)

注意:损坏与断电实验均在隔离实例 6380 上进行,未触碰生产主实例 6379——这类实验永远不要在生产/开发共享实例上做


7. 测试与验收

  • 建议测试:备份/恢复往返(SAVE → 重启 → 校验键数)、redis-check-*命令可用性、损坏文件修复脚本(隔离实例);
  • 生产验收清单见下。

本篇验收清单

  • 能画出 fork + COW 的时序并说出 RDB 的三个阻塞点(fork、页复制、写盘);
  • 能说出appendfsync三档的丢失窗口(0 / ≤1s / 取决于 OS);
  • 知道 7.0+ AOF 是多部件目录结构,base/incr/manifest 各自职责;
  • 知道混合持久化的加载流程(RDB 头 + AOF 尾)与"AOF 优先于 RDB"的规则;
  • 能完成一次 AOF 损坏修复(备份 →--fix→ 重启验证);
  • 能为"秒级容忍/零容忍/可重建"三类业务各设计一套持久化配置。

8. 常见误区

  1. “Redis 默认持久化,重启不丢数据”——默认只 RDB 快照且触发频率低,丢失窗口分钟级起步(§0)。
  2. SAVEBGSAVE一样安全”——SAVE同步阻塞主进程;生产手动触发永远用BGSAVE
  3. “AOF 是日志文件,改一行加一行”——7.0+ 是多部件结构 + 重写机制;直接编辑文件是灾难(§2.2)。
  4. “RDB 损坏了redis-check-rdb --fix能修”——redis-check-rdb只报告不修复;修复靠备份或 AOF(§4.2)。
  5. “实测 kill -9 没丢数据,所以 appendfsync no 安全”——丢失窗口是概率性的,OS 崩溃/断电才是 no 的真正风险(§4.3)。
  6. “开启 AOF 后 RDB 就没用了”——RDB 仍是恢复快、文件小的备份点;混合持久化的 base 就是 RDB 格式(§5)。

9. 本篇小结

回到开篇:持久化的本质是用可控的写路径开销,换取有界的丢失窗口

  • RDB:全量快照,恢复最快,丢失窗口 = 距上次快照;
  • AOF:命令追加 + fsync 策略,丢失窗口有界(everysec ≤ 1s);
  • 混合:base 用 RDB(快加载)+ incr 用 AOF(低丢失),7.x 默认形态;
  • 损坏防御:RDB 靠 CRC 校验拒绝启动,AOF 靠redis-check-aof --fix截断修复——备份永远是最可靠的恢复手段

下一篇 Part 6:过期、内存淘汰与内存优化 把"内存"管起来:TTL 的惰性/定期删除、maxmemory八种淘汰策略、大 key 识别与内存治理,回答"设置了过期时间为什么内存还在涨"。


10. 官方资料

  • Redis persistence 官方文档:https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/
  • RDB 文件格式说明:https://rdb.readthedocs.io/
  • redis-check-aof/redis-check-rdb:https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/
  • BGREWRITEAOF/BGSAVE:https://redis.io/docs/latest/commands/

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询