Redis持久化机制详解
1. Redis 持久化在解决什么问题
Redis 的核心数据在内存中,进程重启或机器宕机后,内存数据会丢失。持久化的目标是把“可恢复的状态”落到磁盘,使 Redis 在重启后能把数据集恢复出来。
需要先把边界说清楚:
- 持久化解决的是“单机重启可恢复”,不等价于高可用(HA)。高可用需要主从复制、哨兵或集群等机制来应对节点故障。
- 持久化也不等价于“零数据丢失”。不同机制对应不同的数据丢失窗口与性能成本。
Redis 的主流持久化组合是:
- RDB(快照):周期性把数据集做成一个紧凑的二进制快照。
- AOF(追加日志):把写命令按 Redis 协议追加到文件,用重放命令恢复。
- 混合持久化:在 AOF 重写时用 RDB 作为前导(preamble)提升恢复速度。
2. RDB(Redis Database)快照机制
2.1 触发方式:手动与自动
RDB 的核心命令是 SAVE 与 BGSAVE:
SAVE:在主线程直接生成 RDB,会阻塞 服务,一般不建议线上使用。BGSAVE:fork 子进程生成 RDB,主线程继续处理请求,阻塞主要发生在 fork 瞬间。
自动触发由 redis.conf 的 save 规则控制(满足“时间窗口 + 变更次数”就触发一次 BGSAVE):
save 900 1
save 300 10
save 60 10000
除了上述显式触发外,Redis 在某些流程中也可能触发生成 RDB(例如全量复制场景需要快照作为全量同步基础,具体取决于部署与配置)。
2.2 BGSAVE 的关键机制:fork + Copy-on-Write
BGSAVE 的主线是:
- 主进程 fork 出子进程。
- 子进程遍历内存数据结构,把数据序列化写入临时文件。
- 写完后原子替换(rename)到目标
dump.rdb,保证文件切换的原子性。
fork 后父子进程共享同一份物理内存页,操作系统通过写时复制(Copy-on-Write,COW)保证一致性:
- 子进程只读旧数据并写文件。
- 主进程继续处理写请求;当它要修改某个内存页时,该页会被复制出一个副本,主进程修改副本,子进程仍看到原页。
因此,RDB 对线上性能的主要影响来自两点:
- fork 开销:数据集越大,fork 越慢,短暂停顿越明显。
- COW 内存放大:
BGSAVE期间写入越多,被复制的页越多,内存峰值可能显著上涨。
2.3 常用配置项与语义
| 配置项 | 作用 | 典型取舍 |
|---|---|---|
dir / dbfilename |
RDB 目录与文件名 | 落盘目录要有足够磁盘与写入性能 |
rdbcompression |
是否压缩 RDB | 省磁盘、增加 CPU |
rdbchecksum |
是否写入校验和 | 更安全、略增开销 |
stop-writes-on-bgsave-error |
RDB 失败时是否拒绝写入 | 保护数据一致性,但会影响可用性 |
rdb-save-incremental-fsync |
增量 fsync 降低卡顿 | 更平滑,但写盘更频繁 |
2.4 优缺点与适用场景
优点:
- 恢复快:加载一个二进制快照通常比重放大量命令快。
- 文件紧凑:便于离线备份、跨机房传输与灾备演练。
缺点:
- 数据丢失窗口大:快照间隔内的写入在宕机后会丢失。
- fork 与 COW 成本:写入高峰期可能产生延迟尖刺与内存抖动。
适用场景:
- 以缓存为主、允许丢失一段时间数据,且更看重恢复速度与备份便利性。
3. AOF(Append Only File)追加日志机制
3.1 AOF 记录的是什么:写命令的“重放日志”
开启 AOF 后,Redis 会在写命令执行完成后,把该命令按 Redis 协议(RESP)追加到 AOF 缓冲区,并按策略刷盘。重启恢复时,Redis 通过重放 AOF 中的写命令重建数据集。
3.2 写入链路:追加、写文件、fsync
AOF 相关的核心配置:
appendonly yes
appendfsync everysec
appendfsync 决定“刷到磁盘”的强度:
| 策略 | fsync 时机 | 数据丢失窗口 | 性能影响 |
|---|---|---|---|
always |
每条写命令都 fsync | 最小 | 最大 |
everysec |
每秒 fsync(典型默认) | 约 1 秒级 | 折中 |
no |
依赖 OS 自己刷盘 | 不可控 | 最好 |
注意:写入系统调用 write 只保证进入内核页缓存,不等价于真正落盘;fsync 才是把数据尽量推到磁盘的关键步骤。
3.3 AOF 为什么需要重写(rewrite)
AOF 的问题是“越写越大”,而且很多命令是可压缩的,例如:
INCR k执行 100 次,最终状态只需要一条SET k 100。- 对同一个 hash field 多次
HSET,最终只需要保留最终值。
因此 Redis 提供 AOF 重写:把“恢复当前数据集所需的最小命令集合”生成到一个新文件,再用原子替换完成切换。
3.4 AOF 重写的底层流程:子进程生成基线 + 父进程追加增量
可以把 BGREWRITEAOF 的流程拆成三段理解:
- 主进程 fork 子进程:子进程基于当前内存数据生成“基线文件”(rewrite 文件),通常会用更紧凑的方式表达当前状态。
- 重写期间主进程仍在处理写请求:
- 写命令继续追加到旧 AOF,保证现有 AOF 可用。
- 同时写命令也会追加到“重写缓冲区”,用于之后把增量补到新文件。
- 子进程完成后,主进程进入短暂的切换窗口:
- 把重写缓冲区的增量内容追加到新文件末尾。
- 原子替换新旧文件,后续写入切到新 AOF。
这个设计的关键点是:重写不阻塞主线程的正常服务,但 fork 与最终切换阶段仍可能带来短暂抖动。
3.5 AOF 常用配置项与语义
| 配置项 | 作用 | 典型取舍 |
|---|---|---|
appendonly |
是否开启 AOF | 需要更高可靠性时开启 |
appendfsync |
fsync 策略 | everysec 常用折中 |
auto-aof-rewrite-percentage |
触发重写的增长比例 | 文件增大到一定倍数再重写 |
auto-aof-rewrite-min-size |
触发重写的最小体积 | 避免小文件频繁重写 |
no-appendfsync-on-rewrite |
重写期间是否跳过 fsync | 降低抖动,但扩大丢失窗口 |
aof-rewrite-incremental-fsync |
重写文件增量 fsync | 平滑写盘,降低卡顿 |
aof-load-truncated |
AOF 末尾损坏时是否截断加载 | 提升可恢复性,但需评估一致性 |
3.6 AOF 的优缺点
优点:
- 数据更完整:
everysec下通常只丢 1 秒级数据,明显优于周期性快照。 - 可解释性更强:AOF 本质是命令序列,便于用工具检查与修复(例如截断损坏尾部)。
缺点:
- 文件通常更大,恢复速度可能慢(需要重放命令)。
fsync与重写带来的磁盘 I/O 与延迟抖动需要关注。
4. 混合持久化:AOF 重写使用 RDB 前导
4.1 为什么需要混合持久化
纯 AOF 的恢复痛点在于:如果命令很多,重放会比较慢。混合持久化的思路是:在 AOF 重写生成新文件时,先把“当前全量数据”用 RDB 的二进制形式写在文件开头,再把重写期间的增量命令追加在后面。
恢复时:
- 先加载 RDB 前导,快速把全量数据恢复出来。
- 再重放尾部的增量 AOF,补齐最新写入。
4.2 配置与影响
混合持久化通常由 aof-use-rdb-preamble yes 控制。开启后带来的取舍:
- 优点:AOF 恢复速度大幅提升(先 RDB,后少量增量命令)。
- 缺点:AOF 文件开头是二进制 RDB,不再是纯文本命令序列,可读性下降,但这不影响 Redis 的加载。
5. 组合策略与启动优先级
5.1 可以同时开启 RDB 与 AOF
工程上常见做法是:同时开启 RDB 与 AOF(并推荐混合持久化),原因是:
- AOF 提供更小的数据丢失窗口。
- RDB 提供更快的冷启动恢复与更好的备份介质(单文件、紧凑)。
5.2 重启时加载哪个
当同时存在 RDB 与 AOF 时,Redis 通常优先加载 AOF(因为它更“新”)。因此你要保证:
- AOF 文件可用(关注磁盘、权限、损坏修复策略)。
- RDB 作为备份介质要定期验证可恢复性(不要只生成不校验)。
6. 运维排查与验证闭环
6.1 常用命令与检查项
INFO persistence
LASTSAVE
BGSAVE
BGREWRITEAOF
CONFIG GET save
CONFIG GET appendonly
CONFIG GET appendfsync
重点关注:
rdb_bgsave_in_progress/aof_rewrite_in_progress:是否有后台任务在跑。rdb_last_bgsave_status/aof_last_bgrewrite_status:最近一次是否成功。aof_current_size、aof_base_size:AOF 增长与重写阈值是否合理。
6.2 修复工具(离线)
Redis 自带校验/修复工具:
redis-check-rdb dump.rdb
redis-check-aof --fix appendonly.aof
修复操作有破坏性风险,务必先备份原文件,并在隔离环境验证恢复结果。