文章目录
- 前言
- [一、 RDB(Redis DataBase)快照模式](#一、 RDB(Redis DataBase)快照模式)
-
- [1. 1 什么是 RDB?](#1. 1 什么是 RDB?)
- [1.2 触发机制](#1.2 触发机制)
- [1.3 底层核心:写时复制技术(Copy-On-Write, COW)](#1.3 底层核心:写时复制技术(Copy-On-Write, COW))
- [二、 AOF(Append Only File)日志模式](#二、 AOF(Append Only File)日志模式)
-
- [2.1 什么是 AOF?](#2.1 什么是 AOF?)
- [2.2 AOF 与 Redis 单线程模型的协同](#2.2 AOF 与 Redis 单线程模型的协同)
- [2.3 AOF 的"瘦身术"------重写机制(Rewrite)](#2.3 AOF 的“瘦身术”——重写机制(Rewrite))
-
- [2.3.1 重写的底层逻辑](#2.3.1 重写的底层逻辑)
- [2.3.2 异步的重写流程(`bgrewriteaof`)](#2.3.2 异步的重写流程(
bgrewriteaof))
- 结语
前言
在 Redis 的高可用高并发架构中,持久化机制 是保证数据安全性的核心底座。由于 Redis 是内存数据库,一旦服务器宕机或重启,内存中的数据就会全部丢失。为了解决这个问题,Redis 提供了两种主要的持久化方案:RDB(Redis DataBase) 和 AOF(Append Only File)。
下面为你全方位拆解这两种机制的底层原理、优缺点。
一、 RDB(Redis DataBase)快照模式
1. 1 什么是 RDB?
RDB 是 Redis 默认的持久化方式。它是在指定的时间间隔内 ,将 Redis 当前内存中的全量数据 生成一个压缩的二进制快照文件(默认文件名是 dump.rdb)保存到磁盘中。恢复数据时,Redis 直接将该二进制文件读入内存,即可快速恢复到生成快照时的状态。
1.2 触发机制
RDB 的触发分为手动触发 和自动触发:
- SAVE 命令(手动阻塞): 主线程直接执行快照保存,会阻塞当前 Redis 服务器,直到 RDB 文件创建完毕。在阻塞期间,Redis 不能处理任何客户端的读写请求,生产环境严禁使用。
- BGSAVE 命令(手动异步): Redis 主线程会 fork 出一个子进程,由子进程负责将数据写入 RDB 文件,主线程继续处理客户端请求,只在 fork 阶段有短暂阻塞。
- 自动触发(配置驱动): 在
redis.conf中配置如save 900 1(900秒内至少有1个key被修改则触发bgsave)。此外,执行shutdown关闭 Redis、或者进行主从复制全量同步时,Redis 也会自动触发bgsave。
1.3 底层核心:写时复制技术(Copy-On-Write, COW)
在使用 bgsave 时,为了让子进程保存快照的同时主线程还能正常处理写命令,底层依赖了操作系统的 COW(写时复制) 机制:
fork出子进程时,子进程与主线程共享同一块物理内存。- 如果主线程此时只进行读操作,则双方相安无事。
- 如果主线程收到客户端的写命令 要修改某个数据,操作系统会把该数据的物理内存页复制出一份副本,主线程在副本上进行修改,而子进程继续读取原本的内存数据写入 RDB 文件。
- 优点: * 数据恢复极快: RDB 纯粹是内存数据的二进制紧凑全量快照,文件体积小,恢复时直接载入内存,速度远超 AOF。
- 对主线程影响小: 持久化工作全权交给子进程,主线程平时的 I/O 压力极低。
- 缺点:
- 数据丢失风险大: 因为是间隔一段时间触发,如果 Redis 在两次快照之间宕机,这段时间内写入的数据将永久丢失。
- 重构成本高(Fork 消耗): 当内存数据量极其庞大时,
fork子进程这个动作本身就会消耗大量 CPU 资源,甚至导致主线程出现秒级的卡顿。
二、 AOF(Append Only File)日志模式
2.1 什么是 AOF?
AOF(Append Only File)字面意思是"只进行追加的文件"。它的核心思想非常纯粹:Redis 并不直接去保存内存中的数据状态,而是把服务器执行过的每一条"写命令"(如 SET、INCR、DEL 等)记录下来,以日志的形式追加保存到磁盘的文件中。
当 Redis 重新启动时,只需要把这个 AOF 文件里的命令从头到尾"重放"(Replay)一遍,在内存中把这些命令全部重新执行一次,就能完美重建、恢复原有的全部数据。
2.2 AOF 与 Redis 单线程模型的协同
既然 AOF 需要把每条写命令都写入磁盘文件,而磁盘的输入/输出(I/O)速度远远慢于内存,这难道不会把 Redis 视若生命的核心单线程给卡死吗?
在现代 Redis 的底层架构中,对这个问题进行了优雅的解耦:
- 先内存,后磁盘: 当客户端发送一条写命令时,Redis 的主线程会先在内存中完成数据的读写处理,然后再将这条命令写入到 AOF 的相关缓冲区中。这样可以确保绝对不会阻碍当前客户端命令的响应速度。
- 后台线程异步处理: 正如我们之前在线程模型中所学,现代 Redis 引入了额外的辅助线程。将缓冲区中的日志真正写入磁盘文件这一费时的 I/O 操作,主要是由额外的后台线程去异步执行的。核心主线程依然可以心无旁骛地在内存中高速排队处理其他客户端的读写请求。
2.3 AOF 的"瘦身术"------重写机制(Rewrite)
随着业务的长时间运行,AOF 面临着一个无法逃避的物理问题:文件体积会无限膨胀。
举个例子:如果客户端对同一个计数器变量执行了 100 万次 INCR count(自增)操作,AOF 文件就会老老实实地记录 100 万行命令。这不仅会榨干服务器的磁盘空间,更致命的是,当 Redis 重启恢复时,需要去重放这 100 万条命令,会导致整个系统启动极其缓慢。
为了解决这个问题,Redis 引入了 AOF 重写机制(Rewrite)。
2.3.1 重写的底层逻辑
AOF 重写并不是去读取、分析或压缩那个已经变得臃肿的旧 AOF 文件,而是直接读取当前的内存快照。
既然内存中记录的最终结果已经是 count = 1000000,那么重写引擎就会直接在新的 AOF 文件中生成一条 SET count 1000000 命令。通过这种方式,原本 100 万行的历史流水,瞬间被凝练成了 1 行最新的状态命令,文件体积得到剧烈瘦身。
2.3.2 异步的重写流程(bgrewriteaof)
为了让重写过程不干扰正常的业务,Redis 采用了异步处理的方式:
- 当触发重写时,主线程会
fork出一个子进程(在后台运行)。 - 子进程完全独立地去读取当前内存中的数据,并将其转化为最小化的命令集,写入到一个全新的临时 AOF 文件中。
- 双写保证数据完整: 在子进程兢兢业业重写文件的同时,主线程依然在处理新的客户端写请求。这些在这期间产生的"新命令",会被主线程同时写入一个专门的重写缓冲区中。
- 无缝替换: 当子进程把内存数据全部重写完毕后,主线程会把重写缓冲区里积压的这点"增量新命令"追加到新 AOF 文件的末尾。最后,用这个精简后的新 AOF 文件原子性地替换掉那个臃肿的旧文件。
通过将两者的知识点有机整合,RDB与AOF的对比思维框架:
| 维度 | RDB(快照模式) | AOF(日志模式) |
|---|---|---|
| 记录本质 | 记录某个时间点的内存全量状态(静态快照) | 记录过去执行过的每一条写命令(动态流水) |
| 文件体积 | 经过压缩的二进制文件,体积小 | 纯文本操作日志,即便重写后体积也相对较大 |
| 恢复速度 | 极快,直接把数据载入内存即可 | 较慢,必须把所有命令重新执行一遍 |
| 数据安全性 | 较低,容易丢失两次快照之间的漏网数据 | 极高,常态化小步快跑记录,能将损失降到最低 |
| 后台协同 | 由 fork 出的子进程异步创建快照 |
由后台线程 异步刷盘;由子进程异步进行重写瘦身 |
结语
RDB与AOF并非互斥选项,而是互为补充。理解快照的全量快速恢复与日志的增量精细记录之间的权衡,是设计高可用Redis架构时不可或缺的技术判断力。