4. 持久化:RDB / AOF / 混合与恢复
❓ 面试官:Redis 的数据是存在内存中的,如果服务器宕机或重启,数据会丢失吗?你们生产环境是怎么配置的?
频率:🔥🔥🔥🔥🔥
💡 一句话总结(先抛结论): Redis 不会丢失数据(或者说最多丢失极少的数据),因为它支持两种持久化机制将内存中的数据落盘到硬盘:RDB(内存快照) 和 AOF(追加日志)。
📝 详细原理解析:
RDB (Redis Database) - 内存快照:
- 原理:在指定的时间间隔内,把当前内存里的全量数据“拍个照”,生成一个
.rdb二进制文件覆盖旧文件。 - 优点:文件体积小(经过了压缩);恢复速度极快(直接加载二进制数据到内存),非常适合做全量冷备。
- 缺点:会丢失最后一次快照之后的所有修改数据;全量生成快照如果数据量巨大(如几十 GB),会消耗大量 CPU 并在 Fork 子进程瞬间引起主线程阻塞。
- 原理:在指定的时间间隔内,把当前内存里的全量数据“拍个照”,生成一个
AOF (Append Only File) - 追加命令日志:
- 原理:Redis 执行的每一次写操作命令(比如
SET、HSET),都会被追加记录到一个.aof文本文件中。重启时,只要把这些命令重新执行一遍,就能恢复数据。 - 优点:数据安全性极高(可以配置成每秒 fsync 刷盘一次,最多只丢 1 秒数据)。
- 缺点:文件体积越来越大;恢复速度慢(需要重放所有的写命令)。
- 原理:Redis 执行的每一次写操作命令(比如
🌟 面试加分项(生产环境实战): 面试官常追问:“那你们生产环境到底用哪种?” 答:生产环境通常采用 Redis 4.0 引入的混合持久化模式(RDB + AOF)。
- 开启配置
aof-use-rdb-preamble yes。 - 在这种模式下,AOF 重写(Rewrite)时,会把当前内存全量数据以 RDB 二进制格式写入新 AOF 文件的开头,然后把后续增量的命令以 AOF 文本格式追加在后面。
- 这样既保证了重启时的极速恢复(读取开头的 RDB),又保证了数据的高安全性(读取后面的 AOF)。
❓ 面试官:能详细说说 AOF 的三种刷盘策略吗?【中级】
频率:🔥🔥🔥🔥
💡 一句话总结(先抛结论): AOF 有三种将缓冲区日志刷入硬盘的策略:always(每次写都刷,极慢)、everysec(每秒刷一次,折中方案,生产默认选这个)、no(由操作系统决定什么时候刷,不可控)。
📝 详细原理解析: 其实 AOF 写入并不是直接写到硬盘上的 .aof 文件的。由于磁盘 I/O 非常慢,Redis 首先会把命令追加到内存中的 AOF 缓冲区(aof_buf)。什么时候把缓冲区的数据真实落盘(系统调用 fsync),就看配置的 appendfsync 参数了:
always(每次写操作都同步):- 原理:只要执行了一个写命令,立刻触发硬盘
fsync。 - 评价:数据绝对安全,但性能极差,完全失去了 Redis 高性能的意义。一般不用。
- 原理:只要执行了一个写命令,立刻触发硬盘
everysec(每秒同步一次):- 原理:有一个专门的后台异步线程,每隔 1 秒调用一次
fsync把缓冲区数据刷盘。 - 评价:性能几乎不受影响。即使服务器突然断电,最多也只会丢失最近 1 秒钟的数据。(生产环境默认和推荐的配置)
- 原理:有一个专门的后台异步线程,每隔 1 秒调用一次
no(操作系统决定):- 原理:Redis 只管把数据写到缓冲,不主动调用
fsync,什么时候真正写入硬盘完全由 Linux 内核说了算(通常是 30 秒左右)。 - 评价:性能最好,但极不安全,宕机可能丢失大量数据。
- 原理:Redis 只管把数据写到缓冲,不主动调用
❓ 面试官:如果 AOF 文件变得超级大,导致磁盘满了或者恢复太慢怎么办?(AOF 重写机制)【中高级】
频率:🔥🔥🔥
💡 一句话总结(先抛结论): 为了防止 AOF 文件无限膨胀,Redis 提供了 AOF 重写(Rewrite)机制。它的核心原理不是去读取旧的 AOF 文件进行压缩,而是直接读取当前内存中的状态数据,生成一份只包含最新数据的全新 AOF 日志,从而大幅缩减体积。
📝 详细原理解析: 假设我连续执行了 100 次 INCR count。
- 旧 AOF 文件里:老老实实记录了 100 条
INCR count命令。 - 重写机制:Redis 发现当前内存里
count的值是 100,它就不管你过去怎么折腾的了,直接在新 AOF 文件里生成一条终极命令:SET count 100。 - 经过重写后,100 条历史日志被压缩成了 1 条,文件体积自然就急剧减小了。
🌟 面试加分项(底层原理探讨): 面试官可能会问:"AOF 重写也是一个耗时操作,它会阻塞主线程处理正常的读写请求吗?" 绝杀回答: "不会。AOF 重写是由后台子进程 bgrewriteaof 来完成的,不阻塞主线程。 但这里有个坑:子进程重写期间,主线程还在不断接收新的写请求,这些增量的新数据如何同步给新 AOF 文件? Redis 为此设计了一个额外的 AOF 重写缓冲区(aof_rewrite_buf)。在子进程重写的这段时间里,主线程不仅要把新命令写进旧 AOF 的缓冲,还要同时写进这个重写缓冲区。 当子进程把全量数据重写完后,主线程只需把这个重写缓冲区里积压的一点点增量数据追加到新 AOF 文件末尾即可,最后原子地替换掉老文件,整个过程对主线程的阻塞极其短暂。"
❓ 面试官:RDB 做快照的时候(bgsave),能处理客户端正常的写请求吗?如果能,底层是怎么做到不阻塞的?【中高级】
频率:🔥🔥🔥
💡 一句话总结(先抛结论): 能处理。底层依靠的是 Linux 操作系统提供的 COW (Copy-On-Write,写时复制) 机制。
📝 详细原理解析:
- Redis 执行
bgsave命令生成 RDB 快照时,主线程会调用fork()系统调用,生出一个子进程。 - 此时,父子进程共享同一块物理内存数据。子进程负责把这块内存的数据默默地写入
.rdb文件。 - 关键点(COW):如果在子进程写磁盘期间,有客户端向主线程发来了写请求,要修改某个 Key(比如把 "name" 从 "张三" 改成 "李四")。Linux 内核并不会让主线程直接修改原物理内存,而是把这一小块内存页(Page,通常是 4KB)复制一份出来(这就是写时复制)。
- 主线程在复制出来的新内存页上进行修改(改成李四),而子进程依然在旧的、未改变的原始内存页上继续生成快照(读到的还是张三)。
总结: 借助于 COW 机制,主进程既能不停顿地处理新的写操作,子进程也能获得一个在 fork 瞬间的“静止不变的”完整内存快照。不会发生数据读写冲突。