Redis 持久化一篇讲透 RDB、AOF 与混合持久化Redis 用得好不好持久化配置占了很大一块。很多人拿 Redis 当缓存用丢了数据重启恢复就行顶多算一次缓存击穿但一旦你把 Redis 用在订单、会话、排行榜、未读消息这类场景持久化就是你最后的底牌。RDB、AOF、混合持久化这三个词面试八股里被翻来覆去地问也是生产环境里最容易踩坑的点。这篇文章我会把 RDB、AOF、混合持久化从原理、配置、触发时机、故障恢复、面试答题脉络全部拆开讲结合我在实际业务里被坑过、排查过的真实经历尽量让不懂 Redis 的人也能听懂让已经用过的人能找到自己之前没注意到的那层细节。1. 为什么 Redis 需要持久化先搞清楚你要防的是什么很多同学理解持久化就是把内存数据存到磁盘。这个方向上没错但理解得太粗。Redis 持久化真正要解决的是进程退出后内存数据怎么恢复。进程退出的方式有两种一种是正常关闭另一种是崩溃比如断电、kill -9、OOM 被系统干掉。正常关闭你可以手动触发保存崩溃时就必须依赖 Redis 平时的持久化机制。如果把 Redis 当成纯缓存数据丢了可以从数据库回源那你确实可以不关心持久化。但生产环境里 Redis 经常承担着不能丢的角色。我这里举几个最常见的场景登录会话 session 存在 Redis重启后用户全部掉线体验极差秒杀预减库存存在 Redis一旦重启库存数据对不上订单可能超卖排行榜、Feed 流、未读数这类重 Redis、轻数据库的数据结构内存一清就是事故。持久化的本质就是在性能和数据安全之间找平衡点。全量备份慢了会影响性能实时备份频繁了又会写太多磁盘。RDB 和 AOF 就是两种不同倾向的解法而混合持久化则试图把两者的优点都拿到。还有一个容易被忽略的点持久化文件不只是应急恢复用的它还承担了数据迁移、灾备、版本升级这些任务。比如你要给 Redis 大版本升级最稳妥的做法就是主从切换或者在低峰期用 RDB 文件做数据迁移。从这角度说持久化不是什么可选项而是基础设施。2. RDB 持久化全量快照简单粗暴但藏着细节2.1 RDB 的核心机制fork 子进程与写时复制RDB 的原理一句话就能说清把当前内存中的全量数据生成一份快照写入一个二进制文件默认叫 dump.rdb。但生成快照的时候 Redis 还在接收写入请求不能直接把内存数据一把梭写进文件所以这里的关键动作是 fork 一个子进程。fork 出来的子进程共享父进程的内存页表子进程负责把数据写入临时 RDB 文件。这里依赖的就是操作系统的写时复制Copy-On-WriteCOW机制。在子进程生成快照期间如果父进程收到写请求需要修改某个内存页操作系统会先把这一页复制一份给父进程父进程在副本上修改子进程读到的仍然是快照开始那一刻的旧数据。这就是全量快照但不阻塞写入的原理。这个机制听起来很顺但要留意一个隐藏风险在生成 RDB 的期间如果写入量很大父进程会复制大量内存页内存占用可能瞬时翻倍。我见过一台 8G 的 Redis 实例在高峰期触发 BGSAVE内存从 5G 直接飙到 7G 多差点触达内存上限。所以在规划内存容量时一定得给 RDB 留出 COW 的内存余量。这也是为什么很多人建议 Redis maxmemory 不要设满留 20%-30% 的余量。2.2 RDB 的三种触发方式手动、自动、关闭前RDB 的触发方式分三种我按生产使用的频率给你拆解。第一种是手动触发就是执行SAVE或BGSAVE命令。SAVE是同步操作会阻塞 Redis 主进程数据量大时直接卡住所有请求生产基本不用。BGSAVE是后台异步执行fork 完子进程后就立即返回这才是生产里该用的命令。第二种是自动触发由配置文件里的 save 参数控制。比如默认配置save 900 1 save 300 10 save 60 10000意思是900 秒内至少 1 次写操作、300 秒内至少 10 次写操作、60 秒内至少 10000 次写操作满足任意一条就触发一次 BGSAVE。自动触发虽然省心但要注意它是按秒数和写入次数双条件匹配的如果写入频率很低可能长时间不触发快照极端情况下会丢失较多数据。第三种是特殊触发包括 Redis 正常关闭时shutdown 且未开启 AOF会执行一次 RDB 保存以及主从复制场景下从节点第一次全量同步时会生成 RDB 传给从节点。这里有个坑有些人以为主从同步是单独机制其实底层就是 RDB 文件传输。2.3 RDB 的核心配置与文件处理RDB 的配置主要集中在 redis.conf 里常用的有这么几个配置项说明建议save自动触发条件按业务写入量自定义stop-writes-on-bgsave-errorBGSAVE 失败后是否拒绝写入保持 yes提前暴露磁盘问题rdbcompression是否压缩 RDB 文件默认 yes建议保持rdbchecksum是否校验 RDB 文件完整性默认 yes不要关dir文件保存目录建议单独挂载磁盘dbfilenameRDB 文件名默认 dump.rdb可自定义说一个我在生产中踩过的真实案例某服务的 Redis 配置里 stop-writes-on-bgsave-error 被改成了 no结果磁盘空间满了BGSAVE 连续失败Redis 还在继续写入。过了一周磁盘被清出来触发了一次成功的 BGSAVE旧数据全部被覆盖成最新的内存状态历史数据早就丢没了。等发现时只能从数据库回放补偿折腾了整整一天。所以我的建议很明确保持 stop-writes-on-bgsave-error 为 yes。这选项的意思就是快照失败就别让我写了先报警再说宁可短暂拒绝写入也别让数据在无声无息中丢失。另外一个容易踩的点是 dump.rdb 文件是先写临时文件再原子改名的所以不会出现写到一半的脏文件。但如果目录磁盘满了改名也可能失败。生产上最好给 Redis 的数据目录单独挂一块盘别和系统盘日志盘挤在一起。2.4 RDB 的优缺点一句大实话RDB 最大的优点是恢复速度快、文件紧凑、适合全量备份和灾备传输。二进制格式加载起来就是内存数据重建比 AOF 重放几万条写命令快得多。最大的缺点是数据丢失窗口大。以默认配置为例如果 900 秒内只有 1 次写入才触发快照那一次写入完成后 Redis 立刻崩溃这条写入就丢了。你可能会说那把 save 条件调小点不就行了调小了确实丢得少但快照频率上来了COW 内存开销和磁盘 IO 压力都会上来需要在业务容忍度之间做取舍。3. AOF 持久化追加日志数据安全优先但别忽略重写3.1 AOF 的写回策略always、everysec、no 到底怎么选AOF 的核心思路和 RDB 完全相反不保存全量数据而是把每一条写命令追加到日志文件里。重启时把日志里的命令重新执行一遍就恢复了数据。但每一条写命令都追加肯定不能每条都同步刷磁盘否则性能扛不住。所以 Redis 提供了三种 appendfsync 策略always每次写命令执行完立刻调用 fsync 把数据刷到磁盘。最安全最多丢一条命令的数据但性能开销最大。实测在高写入场景下吞吐量会明显下降只适合对数据安全要求极高且写入量不大的场景。everysec每秒调用一次 fsync把这一秒内积累的写命令批量刷盘。这是默认和推荐配置最多丢一秒的数据性能影响相对可控。no不主动 fsync由操作系统决定什么时候刷盘通常是缓冲区满或系统调度。性能最好但崩溃时可能丢好几秒甚至更多数据因为内核缓冲区里攒了一堆没落盘的内容。说句实在话生产环境绝大多数场景选 everysec 就够了。我见过有同学为了绝对不丢数据用 always结果高峰期 Redis 延迟从 1ms 涨到 20ms反而拖垮了业务。选哪种策略本质就是回答一个问题丢多少数据你能接受能接受一秒就 everysec一秒都不能丢且写入量不大用 always完全不敏感选 no。3.2 AOF 文件膨胀与重写机制AOF 是追加写命令时间一长文件必然膨胀。举个例子一条 key 被 set 了一万次AOF 里就有一万条 set 命令。恢复的时候要执行一万次但最终结果和只留最后一条 set 是完全一样的。所以 Redis 提供了 AOF 重写机制把当前内存中的数据转换成最精简的写命令生成一个新的 AOF 文件替换掉旧的庞大文件。手动触发用BGREWRITEAOF。自动触发则由两个参数控制auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb意思是当 AOF 文件大小超过上次重写后大小的 100%且文件至少 64MB 时触发自动重写。这两个条件缺一不可都是为了别在文件还小的时候频繁重写也别让文件无限膨胀。重写的过程同样依赖 fork 子进程子进程先根据当前内存生成新 AOF 文件。但这里有个微妙的问题重写期间父进程还在接收新的写命令这些命令会同时写到旧 AOF 文件和一个重写缓冲区rewrite buffer里。等子进程重写完成后父进程会把缓冲区里的增量命令追加到新 AOF 文件末尾用新文件原子替换旧文件。这个逻辑我必须强调一遍因为很多讲 AOF 的文章没说清楚重写缓冲区的作用没有这个缓冲区重写期间的新写入就丢了。正式因为有这个设计AOF 重写期间依然能保证数据不丢只是这段时间内会有额外的内存和 IO 开销。3.3 AOF 文件损坏了怎么办redis-check-aof 实操AOF 是文本协议格式理论上每一行都是一条命令。如果机器突然断电AOF 文件最后可能有一半的命令没写完比如写到一半的字符串。Redis 启动时如果检测到 AOF 文件不完整默认行为是报错退出不会自动截断怕的是你误以为数据是完整的。这时就用得上官方自带的修复工具redis-check-aof --fix appendonly.aof它会扫描 AOF 文件找到第一条不完整的写命令把所有不完整的内容截断掉保留完整命令然后你就可以正常启动了。代价是文件末尾那部分未完成的写命令会丢失。实际操作中修复前最好备份原始文件避免工具误判把有效数据截多了。如果你手边没有 redis-check-aof也可以手工处理AOF 文件是文本可以用 tail 看最后几行确认是不是某条命令被截断了。但生产环境还是建议用官方的检查工具手工处理太容易出错。3.4 AOF 持久化文件格式示例简单看一眼 AOF 文件的格式对理解恢复机制很有帮助。文件里存的并不是原始命令而是 Redis 序列化协议RESP格式的命令类型为*开头表示数组长度$开头表示批量字符串长度。例如执行SET user:name zhangAOF 文件里会记录类似这样的内容*3 $3 SET $9 user:name $5 zhang启动时 Redis 把每行解析出来重新执行一遍数据就回来了。这就是为什么 AOF 恢复比 RDB 慢毕竟是把逐条命令重放一遍。4. 混合持久化RDB 与 AOF 的结合生产首选4.1 混合持久化的由来与原理如果你既想要 RDB 的快速恢复又想要 AOF 的较少数据丢失怎么办Redis 4.0 给出的答案是混合持久化。它的核心思路是当 AOF 触发重写时不再只生成纯 AOF 命令流而是直接把当前内存的数据以 RDB 格式写入 AOF 文件的前半部分再把重写期间产生的增量命令以 AOF 格式追加到文件后半部分。开启方式就一行配置aof-use-rdb-preamble yesRDB 部分是二进制快照加载速度极快AOF 部分是增量命令保证重写之后的数据完整。整个文件本质上还是一个 .aof 文件Redis 启动时先识别文件头部的 RDB 格式快速加载全量数据再顺序执行尾部 AOF 增量命令。加载速度比纯 AOF 快好几个量级数据完整度又比纯 RDB 高。这里要提醒一句如果配置了aof-use-rdb-preamble yes你打开 AOF 文件会发现开头是一堆二进制乱码这是正常的别觉得文件坏了。只有当你看到REDIS开头的魔数才说明文件是混合格式。4.2 为什么我在生产环境推荐混合持久化我自己的实践经验是从 Redis 4.0 开始新项目一律开混合持久化。理由很简单——AOF 保证最少丢数据RDB 段保证恢复速度。这两点并不矛盾混合持久化把它们缝在了一起。对比一下三个方案的恢复时长你就有直观感受了。假设一个 10GB 内存的 Redis 实例数据量约 5GB纯 RDB加载二进制快照大约 20-30 秒纯 AOF重放数百万条写命令可能需要 5-10 分钟甚至更久混合持久化先加载 RDB 段20-30 秒再执行少量 AOF 增量命令几秒内完成整体在 30 秒左右。正因为在恢复速度和数据安全之间找到平衡点混合持久化几乎成了服务器端 Redis 的主流默认选择。如果你还在用纯 AOF在数据量很大的情况下一次宕机恢复等上十几分钟业务方电话能打爆你手机。4.3 混合持久化文件结构简析如果你的 AOF 文件是混合格式它的结构大致是| RDB 二进制数据 | AOF 增量命令流 |RDB 部分以一个 5 字节的REDIS魔数开头后面是序列化的 RDB 数据。AOF 部分是标准的 RESP 命令流。Redis 启动时先尝试按 RDB 格式解析文件头发现REDIS魔数就走 RDB 快速加载否则当作纯 AOF 逐条重放。这个设计确实聪明关键是向后兼容老版本的 Redis 不认识混合格式会用纯 AOF 解析方式来读所以低版本在加载混合文件时会直接报错或不识别。因此跨版本迁移时要注意如果从 Redis 4.0 以上降级到 3.x必须先把混合持久化关掉并重写 AOF 文件否则老版本启动不了。5. RDB、AOF、混合持久化一张表看清怎么选讲了这么多最后还是要落到实际决策上。我整理了一张对比表你在面试也好、做技术方案也好直接拿它当参考就好对比维度RDBAOF混合持久化文件格式二进制快照命令追加日志RDB AOF 双段数据安全性可能丢较多数据always 最多丢一条everysec 最多丢一秒与 AOF 一致恢复速度快慢命令逐条重放快RDB 段加速文件体积紧凑膨胀明显需定期重写重写后体积与 RDB 接近对性能的影响fork 时内存翻倍风险everysec 下影响小与 AOF 重写类似适用场景灾备、迁移、从库全量同步对数据完整性要求较高的业务生产环境通用推荐选型结论我的建议很直接如果只是做缓存数据丢了可以从别处恢复开 RDB 就够了省心如果数据不能丢比如订单、会话、计数类场景开 AOF如果可以开混合持久化Redis 4.0别犹豫直接选它。但是要补一句混合持久化本身也依赖 AOF 机制也就是说你仍然需要关注 AOF 重写、文件膨胀这些问题。它不是银弹只是把 RDB 和 AOF 的优点做了整合。6. 数据恢复流程与优先级重启时到底谁说了算Redis 启动时加载持久化文件的优先级是面试必问的一个细节。规则是这样的如果开启了 AOF 持久化且 AOF 文件存在优先加载 AOF 文件如果 AOF 文件不存在或加载失败尝试加载 RDB 文件如果 AOF 和 RDB 都没有Redis 直接以一个空实例启动。为什么要这样设计因为AOF 文件的数据完整度一定不低于 RDB 文件。如果两个文件都有加载 RDB 可能丢掉 AOF 里更新的数据加载 AOF 则不会比 RDB 少。所以 Redis 给了 AOF 绝对的优先级。这里留个生产经验的提醒如果你修改了持久化配置比如从 AOF 切到 RDB或者反过来最好先停掉 Redis把旧的持久化文件备份走再清空数据目录里的残留文件。否则可能出现这样的情况你关了 AOF 想用 RDB但旧 appendonly.aof 还在目录里Redis 启动时发现 AOF 文件存在优先加载它和你的预期完全相反。7. 面试高频问题怎么答才能让面试官点头7.1 必问题RDB 和 AOF 怎么选面试官问这个不是真让你背配置而是看你有没有取舍思维。我惯用的回答套路是先给结论取决于业务对数据丢失的容忍度。能接受分钟级丢失选 RDB恢复快、文件紧凑不能接受丢失或最多容忍一秒选 AOF everysecRedis 4.0 以上直接选混合持久化兼顾恢复速度和数据完整性。再补一句缓存场景不需要持久化业务数据场景必须持久化。最后用实例佐证如果 Redis 里存的是订单数据我会开 AOF 或混合如果是热点数据回源成本低RDB 就够。这套回答把结论-理由-场景-经验全带上了面试官很难挑毛病。7.2 加分题RDB 阻塞吗fork 的 COW 原理说清楚很多背过八股的同学会说BGSAVE 不阻塞主进程这句话只说对了一半。准确说法是BGSAVE 的 fork 过程是阻塞的fork 期间 Redis 主进程无法处理请求只不过 fork 本身很快通常毫秒级。如果实例很大fork 时间会拉长极端情况可能阻塞上百毫秒。COW 的加分表述是fork 后子进程共享父进程内存页父进程写入时触发缺页中断复制对应内存页。因此 BGSAVE 期间如果写请求很多Redis 实际内存占用会暂时上升。这个点通常面试官会眼前一亮因为不是死背答案的人能说出来的。7.3 压轴题AOF 重写会不会丢数据AOF 重写期间Redis 会同时向旧 AOF 文件写入新命令并存一份到重写缓冲区。子进程完成新 AOF 生成后父进程把缓冲区内容追加到新文件末尾再原子替换。所以 AOF 重写期间不会丢数据但会有额外的内存和 IO 开销。答清楚为什么不会丢就抓住了 AOF 整个设计的关键。7.4 进阶题AOF 文件写入时突然断电会怎样这个问题考察的是对操作系统 fsync 的理解。答案是取决于 appendfsync 策略。everysec 模式下最多丢失最后一次 fsync 之后下一秒内的数据。Redis 启动时会检测到 AOF 末尾存在不完整写入可以手动用 redis-check-aof --fix 修复截断不完整部分。极端情况修复后可能丢失末尾少量命令但完整命令不会受影响。7.5 压轴实战题子进程生成 RDB 时父进程有写操作怎么办这题就是考察刚才说的 COW。答案父进程写操作会触发内存页复制父进程在自己的副本上改子进程读的是快照时刻的旧页。所以 BGSAVE 不会阻塞父进程的读写但内存占用会临时上涨。这也是大容量实例做 BGSAVE 时要注意内存余量的原因。8. 生产环境常见故障与排查实录8.1 磁盘写满导致 BGSAVE 失败现象日志里反复出现Background saving errorfork失败或写临时文件失败。原因大多是数据目录所在磁盘满了或者 inode 耗尽。排查顺序先df -h看磁盘占用再df -i看 inode 是否耗尽。如果是大 key 触发的 RDB 文件临时膨胀也要考虑是不是临时文件还没改名就占了双倍空间。处理办法就是及时扩容、清理无用 RDB 历史文件。千万别只盯着 dump.rdb 一个文件RDB 生成时有临时文件会短时占用和最终文件差不多大小的空间。8.2 AOF 文件持续膨胀拖慢启动现象Redis 重启后恢复要花很长时间甚至恢复期间请求全部超时。原因AOF 长时间没触发重写文件里积累了海量重复命令。检查auto-aof-rewrite-percentage和min-size配置是否符合业务写入量。我见过一个实例写入量很大但 min-size 被调到了 1GB文件涨到 5GB 才重写启动恢复花了二十多分钟业务全挂。处理办法低峰期手动执行BGREWRITEAOF并调整自动重写阈值。8.3 子进程频繁 fork 导致延迟毛刺现象监控里 Redis 平均延迟不高但 p99 延迟经常出现几十毫秒的尖刺。原因RDB 或 AOF 重写触发过于频繁每次 fork 都短暂阻塞主进程。排查思路查看lastsave、rdb_last_bgsave_status、aof_last_bgrewrite_status等指标确认最近触发频率。如果是save参数过于激进放宽条件如果是业务写入量本身就大考虑错峰或者换更高性能磁盘。8.4 从库全量同步卡住检查什么主从复制时从库需要主库生成 RDB 全量文件。如果大 key 多、网卡带宽打满同步就会很慢。排查时先看主库是否在BGSAVE在看从库的复制偏移量是否在前进。如果一直不变八成是网络带宽或磁盘 IO 瓶颈。常用解法主库磁盘用 SSD、从库挂载更快磁盘、错开业务高峰做全量同步。9. 我的实操经验与最后提醒写到这里核心内容基本都讲完了。最后分享三个我踩过坑之后沉淀下来的习惯希望对你有帮助。第一持久化配置一定要纳入监控。不只是看磁盘空间还要盯 BGSAVE 和 AOF 重写是否成功、fork 耗时、AOF 文件增长速度。这些指标可以直接用 INFO 命令拿到做成告警项也不复杂。别等磁盘满了、数据丢了再回头看。第二持久化文件的备份不能省。RDB 文件或 AOF 文件至少保留最近两份并且异地拷贝一份。有人觉得 Redis 持久化就是为了本地恢复忘了它还能用来做灾备和迁移。万一机器整个坏了本地文件全没了你才会意识到异地备份有多重要。第三版本升级前先手动 BGSAVE 一把。每次给 Redis 做版本升级或者 config rewrite 之前手动执行一次 BGSAVE 并确认成功再改配置。很多莫名其妙的升级后数据丢了问题其实不是新版本 bug而是旧环境里持久化本来就是坏的升级重启后问题终于暴露了。Redis 持久化这层东西看起来就是几行配置但背后牵扯到 fork、COW、fsync、原子写、数据完整性这些操作系统级的细节。真正理解它不光是为了应付面试更是为了在生产事故面前能快速判断问题出在哪。毕竟数据能不能找回来往往就取决于你平时对这几个文件的态度。