Redis 持久化完全指南:从 RDB、AOF 到 MP-AOF

前言

Redis 作为内存数据库,持久化是保证数据不丢失的基石。它提供了两种原生持久化方式:RDB(快照)AOF(追加日志) ,并在 4.0 版本之后推出了 混合持久化 ,7.0 版本又引入了 MP-AOF 架构。本文会从原理、配置、优缺点到底层实现细节(比如写时复制到底是谁触发的)一步步讲清楚,让你彻底搞懂 Redis 的数据安全机制。


一、RDB:内存快照

1.1 是什么?

RDB 会把当前 Redis 内存中的数据生成一份二进制快照 ,保存到 .rdb 文件中(默认 dump.rdb)。可以通过 redis.conf 配置路径和文件名,也可以在运行时用 CONFIG SET 动态调整。

  • 文件特点 :每次生成的新 RDB 文件会覆盖旧文件。
  • 恢复速度:因为是直接加载二进制数据,所以恢复非常快。

1.2 触发方式

方式 命令/配置 特点
手动同步 SAVE 主进程执行,阻塞所有客户端,仅用于维护或调试
手动异步 BGSAVE fork 子进程执行,主进程继续服务。推荐手动触发时使用
自动触发 save <秒> <修改次数> 例如 save 900 1:15分钟内至少1次修改就后台执行 BGSAVE

Redis 默认配置:

text 复制代码
save 900 1
save 300 10
save 60 10000

1.3 底层原理:写时复制(Copy-On-Write)

执行 BGSAVE 时,主进程会 fork() 一个子进程。fork 之后:

  • 父子进程的虚拟地址 指向相同的物理内存 ,页表项被标记为只读
  • 子进程负责遍历内存、生成 RDB 文件(只读操作,不会触发 COW)。
  • 父进程 收到写命令(如 SET)需要修改某内存页时,CPU 检测到只读标志 → 触发缺页异常 → 内核为父进程分配新物理页,并复制原页内容 → 父进程在新页上写入。
  • 结果:子进程仍持有原物理页(快照一致性),父进程使用新页(写入新数据)。

❗️ 常见误解:是父进程的写操作触发 COW,而不是子进程。子进程在 RDB 期间从不修改内存。

1.4 优点与缺点

优点 缺点
恢复速度快(直接加载二进制) 数据丢失风险高:两次快照之间的数据会丢失
文件紧凑,适合备份与异地容灾 fork() 开销大:大内存时页表复制可能阻塞主进程数百毫秒
对写性能影响较小 COW 期间大量写入会导致内存翻倍(每个修改页都要复制)

1.5 适用场景

  • 可以容忍几分钟数据丢失(如缓存、日志分析)
  • 对恢复速度要求很高
  • 不希望有太多磁盘 I/O 和文件体积

二、AOF:追加日志

2.1 是什么?

AOF(Append Only File)以日志形式记录每个写命令(实际存储的是 Redis 序列化协议 RESP 格式)。当 Redis 重启时,会重放 AOF 文件中的所有命令来重建数据。

  • 文件名默认 appendonly.aof
  • 追加写,不会覆盖旧内容,因此文件会越来越大。

2.2 写入与落盘流程

  1. 主线程执行写命令 → 修改内存数据。
  2. 将命令追加到内存中的 AOF 缓冲区aof_buf)。
  3. 根据 appendfsync 策略,由后台线程 (或主线程,取决于策略)将缓冲区数据调用 fsync 写入磁盘。

appendfsync 三种策略:

策略 行为 性能 数据丢失风险
always 每个写命令后都 fsync 最差 最多丢失一个命令
everysec(默认) 每秒 fsync 一次 平衡 最多丢失 1 秒数据
no 由操作系统决定何时 fsync 最好 可能丢失大量数据

💡 注意:everysec 策略下,fsync 由后台线程执行,不阻塞主线程

2.3 AOF 重写(Rewrite)

AOF 文件会记录重复或无效命令(例如对同一个 key 反复 SET),导致文件膨胀。重写机制会生成一个最小命令集的新 AOF 文件来替换旧文件。

关键认知 :AOF 重写不是基于旧的 AOF 文件进行修改或压缩 ,而是子进程直接遍历当前内存中的数据,生成能重建当前数据状态的最精简命令,写入一个临时文件。最后主进程把重写期间的增量命令补上,再原子替换。

  • 手动触发BGREWRITEAOF
  • 自动触发条件 (需同时满足):
    • 当前 AOF 文件大小 > auto-aof-rewrite-min-size(默认 64 MB)
    • 当前文件比上次重写后的大小增长了 100% (由 auto-aof-rewrite-percentage 控制)
重写流程(以未开启混合模式为例)

下图清晰展示了内存操作与磁盘落盘的区分:
磁盘 操作系统 子进程 主进程 客户端 磁盘 操作系统 子进程 主进程 客户端 触发 bgrewriteaof 根据内存数据生成精悍命令 loop [处理写命令] par [子进程执行重写(不读旧AOF)] [主进程继续服务] fork() 系统调用 子进程创建成功 启动 遍历共享内存(只读) 写入临时新AOF文件(纯命令) 写命令 修改内存(可能触发COW) 将命令追加到旧AOF文件(根据appendfsync) 将命令写入 aof_rewrite_buf 重写完成 将 aof_rewrite_buf 中增量命令追加到临时新AOF文件 原子替换(rename)旧AOF文件

核心点

  • aof_buf:常规缓冲区,负责将命令刷到旧 AOF 文件(重写期间旧文件依然在正常追加)。
  • aof_rewrite_buf:重写专用缓冲区,记录重写期间的所有增量命令,待子进程完成后追加到新 AOF 文件尾部。

2.4 优点与缺点

优点 缺点
数据更安全everysec 最多丢一秒数据 AOF 文件通常比 RDB 大
支持时间点恢复(配合外部工具) 恢复速度慢(需重放所有命令)
AOF 文件是纯文本,可读、可审计 持续写入磁盘,对 I/O 压力较大
重写时 fork 同样有 COW 内存开销,且 aof_rewrite_buf 可能积压导致 OOM

2.5 适用场景

  • 对数据完整性要求高(支付、交易、用户资产)
  • 可以接受较长的重启恢复时间
  • 希望有操作日志用于审计

三、混合持久化(Redis 4.0+,7.0 前的主流模式)

3.1 是什么?

混合持久化是AOF 重写 的一种增强模式。当执行 BGREWRITEAOF 时,生成的 AOF 文件不再是纯文本命令,而是:

复制代码
[RDB 二进制快照] + [后续的增量 AOF 命令]

这样既保留了 RDB 快速恢复的优点,又结合了 AOF 数据丢失少的特性。

3.2 开启方式

text 复制代码
appendonly yes
aof-use-rdb-preamble yes

3.3 工作流程(重点:aof_buf 和 aof_rewrite_buf 同时存在)

磁盘 操作系统 子进程 主进程 客户端 磁盘 操作系统 子进程 主进程 客户端 loop [每个写命令] par [子进程: 写RDB头部] [主进程: 服务写命令] fork() 子进程创建 启动 遍历共享内存(只读) 以RDB格式写入临时新AOF文件(头部) 写命令 修改内存(触发COW) 命令追加到旧AOF文件(aof_buf → 落盘) 命令也写入 aof_rewrite_buf RDB头部完成 将 aof_rewrite_buf 增量命令追加到临时文件(尾部AOF) 原子替换旧AOF文件

关键问题 :重写期间,aof_bufaof_rewrite_buf 保存相同 的命令文本。高写入场景下,两个缓冲区同时膨胀,内存占用翻倍,极易引发 OOM

3.4 恢复流程

Redis 启动加载 AOF 文件时,检查文件开头是否有 REDIS 魔数:

  • 有 → 混合文件:先快速加载 RDB 部分,再重放尾部 AOF 命令。
  • 无 → 纯 AOF 文件:重放所有命令。

四、MP-AOF(Redis 7.0+,需主动开启)

4.1 设计目标

彻底解决混合持久化中 aof_rewrite_buf 导致的内存翻倍问题,消除 OOM 风险。

4.2 如何开启

注意 :MP-AOF 与旧版混合持久化互斥。要启用 MP-AOF,必须关闭 aof-use-rdb-preamble

text 复制代码
appendonly yes
aof-use-rdb-preamble no

4.3 文件结构

不再是单一的 appendonly.aof,而是一组文件:

文件类型 命名示例 内容
BASE 文件 appendonly.aof.1.base.rdb.aof 全量快照(开启混合模式时为 RDB,否则为 AOF 命令)
INCR 文件 appendonly.aof.1.incr.aof 增量 AOF 命令
MANIFEST 文件 appendonly.aof.manifest 清单文件,记录当前激活的 BASE 和 INCR 文件组合

由于 aof-use-rdb-preamble no,BASE 文件此时是纯 AOF 格式(.base.aof)。但你可以选择开启混合模式(yes)让 BASE 变成 RDB,不过那会退回到旧版单文件模式。所以 MP-AOF 下 BASE 实际是 AOF 格式。

4.4 重写流程(去掉了 aof_rewrite_buf)

磁盘 操作系统 子进程 主进程 客户端 磁盘 操作系统 子进程 主进程 客户端 不再有 aof_rewrite_buf loop [每个写命令] par [子进程: 生成新BASE文件] [主进程: 服务写命令] 旧文件被标记为历史,后续清理 fork() 子进程创建 启动 遍历共享内存(只读) 将内存数据写入新 base.aof(精简命令) 写命令 修改内存(触发COW) 将命令追加到**新的** incr.aof 文件(实时落盘) 新BASE文件完成 原子更新 manifest 文件,指向新 base + 新 incr

核心变化

  • 增量命令不再积攒在内存缓冲区 ,而是直接写入磁盘上的新 INCR 文件
  • 完全移除了 aof_rewrite_buf,杜绝了 OOM 风险。
  • aof_buf 仍然存在,用于将命令刷入当前活跃的 INCR 文件

4.5 恢复流程

Redis 启动时,读取 manifest 文件,获取当前有效的 BASE 文件和 INCR 文件列表,按顺序加载:

  1. 加载 BASE 文件(全量数据)。
  2. 依次重放所有 INCR 文件中的命令。

五、持久化文件损坏与修复

5.1 AOF 文件尾部截断

常见于机器突然掉电,导致 AOF 文件末尾命令不完整。可通过配置 aof-load-truncated 控制行为:

  • yes(默认):忽略末尾不完整命令,加载有效部分,并打印警告日志。
  • no:拒绝启动,需手动修复。

5.2 手动修复工具

bash 复制代码
redis-check-aof --fix appendonly.aof

工作原理 :逐条解析 AOF 文件,找到第一个无法解析的命令位置,截断该位置之后的所有内容,生成一个结构完整但可能丢失尾部数据的文件。

⚠️ 注意:该工具不能修复文件中间的逻辑错误,也不支持混合持久化文件的头部 RDB 部分(因为 RDB 是二进制格式)。

5.3 RDB 文件校验

bash 复制代码
redis-check-rdb dump.rdb

用于检查 RDB 文件完整性,可输出详细报告。


六、fork 开销与写时复制(COW)深度解析

6.1 fork 为什么昂贵?

fork() 不需要复制物理内存,但需要复制父进程的页表 。对于 10GB 的 Redis 进程,页表大小约 20MB,复制耗时与内存大小成正比(约每 GB 20ms)。在虚拟化环境(特别是 Xen)中,fork() 耗时会更长。

6.2 COW 谁触发?

  • 子进程 (RDB 快照或 AOF 重写)只读共享内存,不触发 COW
  • 主进程收到写命令,修改只读页时触发缺页中断 → MMU 分配新物理页 → 复制数据 → 主进程在新页上写入。

6.3 透明大页(THP)的影响

THP 使用 2MB 大页代替 4KB 小页,fork() 时页表变小,fork() 更快。但 COW 时,每次复制 2MB 数据,导致内存开销剧增。生产环境建议关闭 THP

6.4 监控 fork 耗时

bash 复制代码
redis-cli INFO stats | grep latest_fork_usec

6.5 优化建议

  • 控制单个 Redis 实例内存 ≤ 10GB。
  • 尽量部署在物理机而非虚拟机。
  • 适当调低 AOF 重写触发频率。
  • 关闭 THP。
  • 从节点执行备份,转移 fork 压力。

七、生产环境最佳实践

场景 推荐方案
缓存服务,可容忍分钟级数据丢失 仅 RDB(save 900 1 等)
核心数据,要求最多丢一秒,且 Redis 版本 ≥ 7.0 MP-AOFappendonly yes + aof-use-rdb-preamble no + appendfsync everysec
核心数据,Redis 版本 < 7.0 混合持久化(aof-use-rdb-preamble yes),需监控内存防止 OOM
极端性能要求,可接受大量数据丢失 关闭持久化(仅主从复制)

配置示例(Redis 7.0 MP-AOF)

text 复制代码
appendonly yes
appendfsync everysec
aof-use-rdb-preamble no

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

结语

从 RDB 的快照机制,到 AOF 的命令日志,再到混合持久化与 MP-AOF 的演进,Redis 持久化体系始终在数据安全恢复速度性能开销之间寻求平衡。理解这些机制不仅能帮你做出正确的配置选择,还能在遇到性能瓶颈或数据丢失时快速定位原因。

核心回顾

  • fork + COW 是所有持久化操作的基础,开销不可避免。
  • AOF 重写的本质是基于内存重建,而非修改旧文件。
  • 混合持久化用 aof_rewrite_buf 换来了快速恢复,但牺牲了内存稳定性。
  • MP-AOF 用多文件 + 实时落盘替代了缓冲区,根治了 OOM 问题。

希望这篇博客能帮你彻底掌握 Redis 持久化。Happy Coding!

相关推荐
難釋懷6 小时前
Redis内存回收-过期key处理
数据库·redis·缓存
鱼鳞_6 小时前
苍穹外卖-Day05(Redis)
java·redis
空中海8 小时前
Redis知识图谱和回顾
数据库·redis·知识图谱
洛水水10 小时前
Redis对象类型与底层数据结构
数据结构·数据库·redis
爱莉希雅&&&10 小时前
Redis哨兵模式和主从复制和集群模式搭建与扩容缩容
linux·redis·缓存·集群·哨兵·数据库同步
Devin~Y11 小时前
大厂Java面试实录:Spring Boot微服务 + Redis缓存 + Kafka消息队列 + Prometheus链路追踪 + RAG向量检索
java·spring boot·redis·spring cloud·kafka·rabbitmq·spring mvc
MRSM_0111 小时前
Redis 缓存、队列、排行榜的核心用法
数据库·redis·缓存
Trouvaille ~11 小时前
【Redis篇】Redis 安装与启动:快速搭建一个 Redis 环境
数据库·redis·后端·ubuntu·缓存·环境搭建·安装教程
fengxin_rou11 小时前
【Feed 高并发架构实战】:雪花 ID + 三级缓存 + 计数旁路设计详解
数据库·redis·缓存·架构·事务·并发