Redis 持久化:RDB 与 AOF

举报
yd_227606267 发表于 2026/11/04 08:36:30 2026/11/04
【摘要】 讲透 fork 写时复制、三种刷盘策略与 7.0 多部分 AOF 的实现

Redis 是内存数据库,进程退出后数据就没了。持久化要解决两个问题:重启后能恢复多少数据,以及持久化过程会不会拖慢主线程。本文以 Redis 7.x 为基准,拆解 RDB 的 fork 与写时复制、AOF 的刷盘策略、重写流程,以及 7.0 引入的多部分 AOF 带来的变化。

一、RDB:某一时刻的全量快照

RDB 是把整个数据集序列化成二进制文件,文件以 REDIS0011 魔数开头,依次写入辅助字段、各个数据库的键值对、EOF 和 CRC64 校验和。恢复时直接解析文件重建内存,速度比逐条重放命令快得多。

触发方式有三类:

  • 配置 save <seconds> <changes>,例如默认的 save 3600 1 300 100 60 10000;
  • 手动执行 SAVE(阻塞主线程,禁用)或 BGSAVE;
  • 主从全量同步、SHUTDOWN 时由服务端自动触发。

BGSAVE 会 fork 一个子进程,子进程持有父进程内存的只读视图并写盘,父进程继续处理命令。这里依赖操作系统的写时复制(Copy-On-Write):fork 只复制页表,不复制物理页;父进程每修改一页,内核才为该页复制一份。因此快照期间的内存增量约等于被修改的数据页大小,写越频繁,COW 放大越明显。

fork 本身不是免费的。它要复制页表,1 GB 内存大约耗时 10~20 ms,期间主线程被阻塞。内存越大、页表项越多,停顿越久。

# 观察 fork 与快照状态
127.0.0.1:6379> INFO persistence
rdb_bgsave_in_progress:0
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:0

rdb_last_cow_size 是上一次快照期间 COW 复制的字节数,持续偏高说明写入压力大,需要评估快照频率。

二、RDB 的关键配置

save 3600 1 300 100 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
rdb-del-sync-files no
  • stop-writes-on-bgsave-error yes:快照失败后拒绝写入,避免"以为有持久化、其实没有"。用了监控告警的团队可以关掉,但要确保告警真的有人看。
  • rdbcompression yes:对字符串做 LZF 压缩,省磁盘和网络,代价是少量 CPU。
  • rdbchecksum yes:写入 CRC64,加载时校验,能发现文件损坏。
  • rdb-del-sync-files no:全量同步后是否删除用于同步的 RDB 文件,默认保留。

RDB 的缺点是会丢数据:两次快照之间的写入全部丢失。它适合做冷备、主从全量同步的载体,不适合作为唯一的持久化手段。

三、AOF:把写命令追加到日志

开启 AOF 后,每条修改数据的命令都会以 RESP 协议追加到文件末尾。恢复时从头重放,得到与内存一致的状态。

appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes

appendfsync 决定数据安全性与吞吐的平衡:

| 取值 | 行为 | 最多丢多少 | 代价 |

| --- | --- | --- | --- |

| always | 每条命令都 fsync | 最多 1 条 | 受磁盘 IOPS 限制,吞吐骤降 |

| everysec | 后台线程每秒 fsync 一次 | 约 1 秒 | 磁盘慢时主线程可能被阻塞约 2 秒 |

| no | 交给操作系统刷盘 | 约 30 秒 | 机器宕机丢数据多 |

生产环境通常选 everysec。如果磁盘 fsync 延迟高,Redis 检测到上一次 fsync 未完成时会推迟本次 fsync,主线程在写 AOF 缓冲区时可能被阻塞,表现为周期性毛刺。

四、AOF 重写:把日志压小

AOF 会不断膨胀:对同一个 key 执行 100 次 INCR,日志里就有 100 条命令,而恢复只需要一条 SET。BGREWRITEAOF 就是根据当前内存状态生成一份最小命令集。

重写同样靠 fork 子进程完成:

  1. 父进程 fork 出子进程,子进程遍历内存写新 AOF;
  2. 父进程把期间的新写命令同时写入 aof_buf 和重写缓冲区;
  3. 子进程完成后,父进程把重写缓冲区的内容追加到新文件末尾,再原子替换旧文件。

第 2 步的重写缓冲区是内存开销的主要来源。如果重写期间写入量很大,缓冲区可能撑爆内存,这也是 7.0 改造多部分 AOF 的动机之一。

五、Redis 7.0 的多部分 AOF

7.0 之前,AOF 是单个文件,重写时要同时维护旧文件、新文件和缓冲区,还要处理替换瞬间的写入。7.0 把 AOF 拆成一个目录:

appendonlydir/
├── appendonly.aof.manifest
├── appendonly.aof.1.base.rdb
├── appendonly.aof.1.incr.aof
└── appendonly.aof.2.incr.aof
  • manifest 记录文件清单与顺序;
  • base 是基准文件,aof-use-rdb-preamble yes 时采用 RDB 格式,即混合持久化;
  • incr 是增量命令文件,重写时直接开一个新的 incr 文件,旧文件在合并完成后删除。

好处是重写期间不需要把增量再拷回 base,减少一次内存缓冲和磁盘拷贝;manifest 也让损坏修复的粒度更细。升级到 7.x 后,旧的单文件 AOF 会在启动时自动转换为多部分格式。

六、混合持久化与恢复顺序

混合持久化指 base 用 RDB、增量用 AOF:加载时先快速恢复 RDB 快照,再重放少量增量命令,兼顾速度与数据安全。7.0 起 aof-use-rdb-preamble 默认为 yes,新写入的 base 文件就是 RDB 格式。

启动时的加载顺序是:如果 AOF 开启,优先加载 AOF;否则加载 RDB。因为 AOF 通常更完整,二者同时存在时以 AOF 为准。

# 检查 AOF 文件是否完整
redis-check-aof --fix appendonlydir/appendonly.aof.1.incr.aof

# 查看当前持久化状态
redis-cli INFO persistence | grep -E "aof_enabled|aof_last_write_status|aof_rewrite_in_progress"

aof-load-truncated yes 允许加载被截断的 AOF 尾部并打印警告,适合掉电场景;如果要求严格,设为 no,让启动直接失败,由人工确认。

七、怎么选

  • 只做缓存、能接受重启后回源:可以关闭持久化,或只保留低频 RDB 做冷备。
  • 存业务数据、不能接受明显丢失:AOF everysec + RDB 定期快照,即混合持久化。
  • 追求极致安全:AOF always,但必须确认磁盘 IOPS 能扛住,否则延迟不可接受。

常见坑 / 最佳实践

  1. 务必设置 vm.overcommit_memory=1。 fork 依赖内存超额分配,设为 0 时大内存实例的 BGSAVE 可能直接失败。
  2. 关闭透明大页(THP)。 THP 会把 COW 的复制单位从 4 KB 放大到 2 MB,快照期间延迟明显升高,应在系统层设置 never。
  3. 不要在主库做全量持久化压测。 BGSAVE 的 fork 停顿和 COW 放大都会影响在线请求,压测请在从库或独立实例进行。
  4. 监控 rdb_last_bgsave_status 和 aof_last_write_status。 磁盘满或权限错误会让持久化静默失败,只看进程存活发现不了。
  5. 重写频率要合理。 auto-aof-rewrite-percentage 100 表示文件翻倍才重写,过于频繁的重写会反复 fork,放大延迟抖动。

小结

  • RDB 是某一时刻的全量快照,恢复快但会丢两次快照之间的数据;BGSAVE 依赖 fork 与写时复制,内存越大停顿越久。
  • AOF 记录写命令,everysec 是安全与性能的平衡点;文件会膨胀,需要重写。
  • Redis 7.0 用 base + incr + manifest 的多部分结构替代单文件 AOF,降低重写期间的内存与拷贝开销。
  • 混合持久化(RDB base + AOF 增量)是业务数据的推荐配置,恢复时优先加载 AOF。
  • 持久化的稳定性取决于系统参数:overcommit_memory=1、关闭 THP、磁盘余量与告警缺一不可。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。