Redis 持久化深挖 · RDB、AOF 与写时复制

作者:杨帅      发布日期:2026.09.01

Redis · 持久化引擎

内存怎么落到磁盘

RDB 快照、AOF 日志,与背后那次关键的 fork

Redis 快,因为数据活在内存里;但内存一断电就没了。它靠两件武器把数据刻到磁盘——RDB 快照AOF 日志。而让"边存盘边服务"成为可能的,是一个精巧的机制:fork + 写时复制

▲ BGSAVE:fork 出子进程,靠写时复制"边存边服务"fork + copy-on-write
父进程内存页 · 继续服务 P1 P2 P2 P3 P4 P2′ 新副本 子进程 一致视图 · 写快照 共享内存页(零拷贝) 磁盘 dump.rdb ① fork():子进程与父进程共享全部内存页,零拷贝 ② 父进程改动某页 → 只复制这一页(copy-on-write),其余仍共享 ③ 子进程把一致快照写入磁盘,父进程全程不阻塞
共享页:父子共用,不占额外内存 被改的页:复制一份 子进程写盘
01 · 两件武器

快照 与 日志

数据在内存里易失,一断电就清零。Redis 把它刻到磁盘的方式有两种,思路完全不同:

RDB · 快照

给整个内存拍照片

  • 某一时刻的全量二进制快照
  • 文件紧凑、恢复
  • 风险:丢失"上次快照之后"的写
AOF · 日志

记下每一条写命令

  • 把每个写操作追加进日志
  • 耐用(最多丢约 1 秒)
  • 代价:文件更大、恢复更慢
打个比方

RDB 像定期给房间拍全景照:一张照片还原全貌,但两张照片之间的变化没记下。AOF 像记流水账:每挪动一件东西都写一笔,事后照账本一步步复原。

02 · RDB

给内存拍快照

RDB 是某一时刻内存的全量二进制镜像(dump.rdb)。它由几种方式触发:

# redis.conf:满足任一条件就后台存盘 save 3600 1 # 1 小时内 ≥1 次改动 save 300 100 # 5 分钟内 ≥100 次改动 save 60 10000 # 1 分钟内 ≥10000 次改动 BGSAVE # fork 子进程后台存盘(不阻塞,常用) SAVE # 前台存盘,会阻塞整个服务(几乎不用)

此外,主从全量同步(主库生成 RDB 发给副本)和正常关机也会触发。RDB 的优点是文件小、重启加载快;缺点是两次快照之间的写入,一旦崩溃就丢了

03 · 核心机制

非阻塞快照的秘密:fork 与写时复制

问题来了:给几十 GB 的内存"拍照"要花时间,难道要停下服务干等?不用——这正是 BGSAVEfork + 写时复制(COW) 的精妙之处。

fork 出一个子进程,它和父进程共享同一批内存页(并不真的复制一份数据)。子进程按这个"一致的瞬间视图"把快照慢慢写盘,父进程则继续处理读写。当父进程要改某一页时,操作系统才把那一页复制一份给它改,子进程手里仍是旧值——快照因此保持一致。

所以额外内存开销不等于整个数据集,而是正比于存盘期间被改动的页量。写得越猛,复制的页越多。

!

两个现实代价:fork() 本身会有一次延迟尖峰,且正比于数据集大小(页表越大越明显)——超大实例尤其要留意。② 建议预留 30–50% 的内存余量,避免存盘时因 COW 复制页而触发交换(swap);关闭透明大页(THP),否则每次复制的页更大、抖动更狠。

一句话

写时复制 = "先都共用,谁改了才给谁单独复印一份"。这让 Redis 能边拍照边营业

04 · AOF

记录每一条写命令

AOF 把每个写命令按 RESP 协议追加到日志里,重启时重放一遍就能还原数据。它的耐用度由 fsync 策略决定:

fsync 策略含义取舍
always每个写都刷盘最安全,最慢
everysec每秒批量刷盘最多丢约 1 秒,推荐
no交给操作系统决定最快,最不安全

日志会越写越大(同一个键反复写会留下一堆冗余命令),于是有了重写(rewrite)BGREWRITEAOF(或按 auto-aof-rewrite-percentage 100 / auto-aof-rewrite-min-size 64mb 自动触发)会 fork 子进程,用最少的命令重新生成一份紧凑日志。

▲ 多部分 AOF(Redis 7.0+):base + incr + manifestappendonlydir/
appendonlydir/ appendonly.aof.1.base.rdbRDB 格式的基底快照 appendonly.aof.1.incr.aof增量写命令 appendonly.aof.manifest清单:记录各组件文件
!

升级到 7.0 的一个坑:AOF 不再是单个 appendonly.aof,而是 appendonlydir/ 目录下的多部分文件(base + incr + manifest)。旧的"只备份一个 aof 文件"的脚本会失效——备份 / 恢复要针对整个目录和 manifest

05 · 混合持久化

快照打底,日志收尾

RDB 恢复快但会丢数据,AOF 耐用但加载慢——能不能两者兼得?能,这就是混合持久化aof-use-rdb-preamble yes,现代版本默认开启)。

AOF 重写时,生成的基底文件用 RDB 格式(加载快),后面再接增量的 AOF 命令(保证耐用)。重启时先秒读 RDB 基底、再重放少量增量——又快又几乎不丢

# 重写机制(fork + 差异缓冲) 1. fork 子进程 → 把当前数据写成紧凑的新基底 2. 父进程继续服务 → 新写入既进旧日志、也进重写缓冲区 3. 子进程写完 → 父进程把缓冲区差异追加到新文件 4. 原子替换 manifest,旧文件标记为 HISTORY 清理

生产上被广泛采用的组合就是:混合持久化 + appendfsync everysec——近乎零丢失、重启不算慢、磁盘占用可控。

06 · 怎么配、怎么选

四种组合,按需取舍

方案适合代价
只用 RDB能容忍丢几分钟数据,要快照备份 / 快速重启崩溃丢"上次快照后"的写;周期性 fork 开销
只用 AOF要尽量少丢数据文件更大、加载更慢(无 RDB 基底时)
混合(推荐)绝大多数生产场景基本没有短板,配置略多
都不开纯缓存,丢了无所谓重启即清空

同时开启 RDB 与 AOF 时,重启优先加载 AOF(更完整、更新)。

×
关掉持久化还当数据库用不开任何持久化,宕机即全丢。当主存储时至少要 AOF + 备份。
×
无视 fork 延迟超大实例的 BGSAVE / 重写会有可感知的停顿,容量与时机要规划。
×
内存不留余量存盘期 COW 复制页可能撑爆内存触发 swap,留 30–50% 余量、关 THP。
×
旧脚本备份多部分 AOF7.0 起是 appendonlydir 目录 + manifest,只备份单文件会漏。
收尾 · 一句话

边拍照,边营业

Redis 用 RDB 快照换恢复速度、用 AOF 日志换耐用度,再用 fork + 写时复制让"存盘"不打断"服务"。现代默认的混合持久化把两者的长处合在一起——生产上配上 everysec,就是又快又稳的落盘方案。

RDB 全量快照(快、会丢)· AOF 追加日志(耐用、everysec 最多丢 1 秒)· fork + COW 让后台存盘不阻塞 · 混合持久化 = RDB 基底 + AOF 增量(推荐)