面试官一句“Redis 怎么持久化”,很多人张口就是 “RDB + AOF”,追问到 fork、刷盘策略和日志重写就开始卡壳...

这大概是 Redis 面试里最常见的一道"送命题"。

面试官问:"Redis 怎么持久化?"

你心里一喜,这题我背过:"RDB 和 AOF,RDB 是做快照的,AOF 是记录命令的,两个可以一起用。"

面试官点点头,追问道:"那 RDB 的 fork 具体是怎么工作的?会不会阻塞主线程?AOF 的刷盘策略有哪几种,AOF日志重写流程是怎样的,7.0 前后有什么变化?"

空气突然安静。

如果你也有过类似的经历,或者正在为这样的场景做准备,这篇文章就是为你写的。我们不背八股文,而是把 Redis 持久化的底层逻辑从头梳理一遍------搞懂原理,自然不怕追问。

为什么要有持久化?先理解问题本身

Redis 是一款内存数据库,数据主要存放在内存中,读写速度极快。但内存有一个致命的弱点:一旦进程退出、服务器断电或系统崩溃,内存中的数据就会全部消失。

持久化要解决的问题,本质上就是:如何在性能和数据安全之间找到一个可接受的平衡点。

Redis 提供了两种核心持久化手段------RDB 和 AOF,以及它们的组合方式。接下来,我们逐一拆解。

RDB:一次全量快照

什么是 RDB?

RDB(Redis Database)是 Redis 默认开启的持久化方式。它会在指定的时间间隔内,将内存中的数据集以二进制形式写入磁盘,生成一个名为 dump.rdb 的紧凑文件。

你也可以通过配置文件设定触发条件,例如:

txt 复制代码
save 60 1000

这行配置的含义是:60 秒内如果发生了至少 1000 次写入操作,就执行一次快照。

也可以手动执行 SAVEBGSAVE 命令触发。两者的区别在于:SAVE 会阻塞主进程直到快照完成,生产环境几乎不用;BGSAVE 则是后台异步执行。

面试追问:fork 到底在干什么?

当执行 BGSAVE 或自动触发快照时,Redis 主进程会调用 fork() 系统调用创建一个子进程。这个操作是 RDB 的核心,也是面试官最爱追问的地方。

fork 做了什么?

  • fork() 会复制一份进程描述符,子进程与父进程共享同一份内存页,而不是立即拷贝全部内存数据。

  • 此时父子进程共享内存,后续如果有新的写入请求,操作系统才会为修改的内存页复制一份副本------这就是 写时复制 技术。

这样设计的收益是什么?

  • 创建子进程时不需要复制全量数据,速度很快(但大数据集下 fork 本身仍可能耗时数十毫秒到数百毫秒)。
  • 子进程负责将共享的内存数据写入 RDB 文件,父进程继续处理客户端请求,两者互不干扰。

fork 会造成阻塞吗?

会。fork() 调用本身是由父进程执行的,在 fork 完成之前,父进程无法处理新的请求。虽然写时复制减少了内存拷贝开销,但 fork() 系统调用自身的耗时与进程内存大小正相关。如果 Redis 实例内存达到几十 GB,fork() 可能导致服务暂停几百毫秒甚至 1 秒以上。

这就是 RDB 的一个潜在风险:频繁生成快照时,fork 开销可能影响服务延迟。

AOF:命令日志

什么是 AOF?

AOF(Append Only File)是一种更精细的持久化方式。开启后,Redis 会将每个写命令以 Redis 协议格式追加到 AOF 文件末尾。重启时,Redis 按顺序执行这些命令,重建数据集。

开启方式:

text 复制代码
appendonly yes

面试追问:刷盘策略有哪几种?

这是面试官区分"背过"和"真懂"的关键分水岭。

Redis 提供了三种 appendfsync 配置,控制数据写入磁盘的时机:

策略 行为 安全性 性能
always 每次写入后立即执行 fsync 最高,最多丢失一条命令 最慢,磁盘 I/O 频繁
everysec(默认) 每秒执行一次 fsync,由后台线程负责 较高,最多丢失 1 秒数据 平衡,生产首选
no 不主动 fsync,由操作系统决定刷盘时机 较低,可能丢失数秒数据 最快,接近 RDB

这里需要澄清一个概念:写入 AOF 文件和刷盘(fsync)是两件不同的事。 写文件只是把数据从用户态缓冲区写入内核页缓存,而 fsync 才是真正把数据落盘。如果只写不刷,操作系统可能在崩溃时丢失尚未刷盘的数据。

默认的 everysec 策略在生产环境中使用最广泛------它把数据丢失控制在 1 秒以内,同时保持较高的写入性能。

面试追问:日志重写是什么?7.0 前后有什么变化?

随着写操作不断增加,AOF 文件会越来越庞大。更关键的是,很多历史命令是冗余的------比如对一个键执行了 100 次 INCR,AOF 中记录了 100 条命令,但恢复时只需要最后一条。

日志重写就是为了解决这个问题:它不直接修改旧文件,而是基于当前内存中的数据,生成一套最短的命令序列,写入新文件,然后原子切换。

Redis 7.0 之前:

  • 主进程 fork 子进程,子进程将重建数据集所需的最小命令序列写入临时 AOF 文件。
  • 父进程继续向旧 AOF 文件写入变更,同时将新命令缓存在内存缓冲区中。
  • 子进程完成后,父进程将缓冲区内容追加到新文件末尾,随后重命名替换旧文件。

Redis 7.0 及以后:

  • 主进程 fork 子进程,子进程生成新的 AOF 基础文件。
  • 父进程创建新的增量文件继续写入;即使重写失败,旧基础文件与所有增量文件仍构成完整数据集。
  • 子进程完成后,父进程将新增量文件与新基础文件组装成临时清单,替换生效,同时清理废弃文件。

核心差异一句话概括: 7.0 之前重写期间的新写入先缓存在内存中,重写完成后一并追加;7.0 之后新写入直接落入独立的增量文件,与基础文件通过清单文件分离管理,重写过程更安全、更可控。

RDB + AOF:组合模式

两者可以同时启用。此时 Redis 重启时优先使用 AOF 恢复数据,因为 AOF 的数据完整性更高。同时,RDB 可以作为冷备和快速恢复的补充手段。

一个常见的最佳实践是:

  • RDB 用于定期备份(如每小时、每天),便于异地存储和灾难恢复。
  • AOF 用于保证热数据的安全性。

两者结合,即使 AOF 文件损坏且无法修复,也可以退回到最近的 RDB 快照,再手动合并 AOF 中后续的有效命令。

AOF 文件损坏了怎么办?

这是生产环境中真实可能遇到的问题,面试官也可能以此考察你的实战经验。

情况一:文件末尾被截断(命令不完整)

Redis 默认配置 aof-load-truncated=yes,会忽略末尾不完整的命令并给出警告日志,然后正常启动。

情况二:文件中间出现无法解析的"乱码"

Redis 会报错并拒绝启动。此时需要使用 redis-check-aof --fix 工具进行修复。但要注意:修复会丢弃从损坏位置到文件末尾的所有数据,如果损坏发生在文件开头,数据丢失可能非常严重。因此,操作前务必先备份原文件。

总结:面试官到底想听到什么?

回到开头的问题。当面试官问"Redis 怎么持久化"时,他真正想考察的是:

  1. 你是否知道 RDB 和 AOF 分别是什么? ------ 这是基础。
  2. 你是否理解底层机制? ------ fork、写时复制、刷盘策略、日志重写,至少知道核心概念。
  3. 你是否能根据场景做出合理的选择? ------ 不同业务对数据安全性和性能的要求不同,没有银弹。
  4. 你是否遇到过真实问题? ------ 文件损坏、性能抖动、备份恢复,有实战经验会很加分。

写在最后

Redis 持久化不是一个可以"背熟配置就完事"的话题。它的背后是操作系统进程管理、文件系统同步机制、内存分配策略等多个领域的交汇。面试官追问这些细节,不是为了刁难你,而是想验证你是否真的理解自己在用什么东西。

理解原理的人,不需要背答案。

希望这篇文章能帮你理清 Redis 持久化的全貌,下次遇到这道题,从容应答。