Redis 的数据主要放在内存里,所以它速度很快。
但这也会带来一个问题:
如果 Redis 进程崩溃或者服务器突然断电,内存中的数据怎么办?
如果没有任何持久化机制:
text
Redis 运行
↓
数据只在内存
↓
服务器断电
↓
内存数据丢失
因此 Redis 提供了两种最核心的持久化机制:RDB 和 AOF。
官方把两者概括得很清楚:RDB 保存某个时间点的数据快照,AOF 则记录服务器收到的写操作,并在重启时重新执行这些操作恢复数据。
1. Redis 持久化到底是什么?
所谓持久化,就是:
把原本主要存在内存中的数据保存到磁盘等持久存储中,使 Redis 重启以后可以恢复数据。
Redis 常见的几种选择是:
RDB
AOF
RDB + AOF
或者完全关闭持久化。
如果 Redis 只是纯缓存,底层数据库可以重新构建全部数据,有些场景确实可以不启用持久化。
如果 Redis 本身保存了重要业务数据,就必须认真考虑持久化和备份策略。
2. 什么是 RDB?
RDB 可以理解成:
在某一个时间点,把 Redis 当前的数据集整体保存成一个快照。
例如上午 10 点:
text
Redis 内存:
A = 100
B = 200
C = 300
Redis 生成一次 RDB:
text
10:00 快照
A = 100
B = 200
C = 300
之后数据继续变化:
text
10:03
A = 150
B = 250
C = 300
但是之前生成的 RDB 仍然代表:
10:00 那一刻的数据状态。
所以 RDB 的核心关键词就是:
Snapshot
也就是快照。
Redis 默认的 RDB 文件名通常是 dump.rdb。
3. RDB 怎么触发?
Redis 可以根据配置自动产生 RDB,也可以手动触发。
例如:
conf
save 60 1000
可以理解为:
在满足对应时间和修改次数条件后触发快照。
也可以执行:
bash
BGSAVE
让 Redis 在后台生成 RDB。
还有一个命令:
bash
SAVE
也可以生成快照。
但 SAVE 是同步执行的,在保存期间会阻塞其他客户端,因此生产环境通常不会随便使用;一般更常使用 BGSAVE。
4. BGSAVE 为什么不会一直阻塞 Redis?
执行 BGSAVE 时,Redis 会创建子进程。
大致可以理解:
text
Redis 父进程
↓
fork()
↓
┌───────────────┐
│ │
↓ ↓
父进程 子进程
继续处理请求 生成 RDB
子进程负责把数据写入临时 RDB 文件。
生成完成后,再用新的 RDB 替换旧文件。
所以磁盘写 RDB 的主要工作不会直接由处理客户端请求的父进程完成。
5. fork 以后是不是直接复制一整份内存?
不是简单地立刻把整个内存复制一份。
这里用到了操作系统的 Copy-On-Write,也就是写时复制。
刚刚 fork() 完成时,父进程和子进程可以共享相同的物理内存页。
可以简单理解:
text
fork 后
父进程 ─┐
├── 共享内存 Page
子进程 ─┘
子进程看到的是 fork 那一刻的数据快照。
如果之后客户端执行:
bash
SET user:1 newValue
父进程需要修改某个共享内存页时,操作系统才会复制对应页面:
text
原 Page
↓
发生修改
↓
复制一份
↓
父进程使用新 Page
子进程继续使用旧 Page
这样子进程仍然可以稳定地把 fork 时刻的数据写入 RDB。
这就是 Copy-On-Write。
6. Copy-On-Write 会不会完全没有额外内存开销?
不会。
虽然 fork 时不会立即复制整个 Redis 数据集,但如果 BGSAVE 期间发生大量写操作,越来越多内存页被修改,就会产生越来越多的写时复制。
例如:
text
Redis 数据集很大
+
BGSAVE 正在进行
+
大量 SET / HSET / DEL
↓
大量内存 Page 被修改
↓
Copy-On-Write 增加
↓
额外内存占用上升
所以大数据量 Redis 执行后台持久化时,需要给系统留出足够的内存空间。
此外,大实例执行 fork() 本身也可能带来短暂延迟,因为操作系统仍然需要处理页表等数据结构。Redis 官方也专门把 fork 延迟列为需要关注的性能问题。
7. RDB 最大的问题是什么?
假设:
text
10:00
生成 RDB
然后:
text
10:01
写入数据 A
10:02
写入数据 B
10:03
写入数据 C
但是下一次 RDB 还没有生成。
此时:
text
10:04
💥 服务器断电
那么恢复时只能加载:
10:00 的 RDB。
也就是说:
text
10:00 之后
到宕机之前
这段时间的数据可能丢失。
所以 RDB 最大的缺点是:
它只能恢复到最近一次快照,而不能天然保证恢复到宕机前的最新状态。
官方同样指出,如果只依赖 RDB,在异常停止时需要接受最近一段时间数据丢失的可能。
8. RDB 有什么优点?
虽然数据安全性不如高频 AOF,但 RDB 有很多优点。
首先,RDB 是一个比较紧凑的二进制快照文件,非常适合:
备份
传输
灾难恢复
其次,Redis 重启时直接加载快照,通常会比重放大量 AOF 命令更快。
另外,RDB 平时不需要为每一次写操作都追加一条磁盘日志,因此运行时持久化开销相对较低。
官方也明确把文件紧凑、适合备份和灾备、重启恢复较快列为 RDB 的主要优势。
9. 什么是 AOF?
AOF 全称是:
Append Only File
它的思路和 RDB 完全不同。
RDB 记录:
某一个时间点 Redis 中有什么数据。
AOF 更关注:
Redis 执行了什么写操作。
例如依次执行:
bash
SET count 1
INCR count
INCR count
AOF 会记录这些导致数据发生变化的操作。
重启以后:
text
读取 AOF
↓
重新执行这些写操作
↓
SET count 1
↓
INCR count
↓
INCR count
↓
count = 3
最终重新构建出原来的数据集。
10. AOF 会记录 SELECT 吗?
通常不会。
例如:
bash
GET user:1
不会改变 Redis 数据。
AOF 真正关心的是:
会改变数据集状态的写操作。
例如:
SET
HSET
LPUSH
DEL
等。
因为 Redis 重启恢复时,只需要知道:
数据是怎么一步一步变成现在这个状态的。
查询命令没有必要参与这个过程。
11. AOF 是每执行一条命令就立刻写磁盘吗?
这里一定要区分:
write
和:
fsync
它们不是一回事。
大致可以理解:
text
执行写命令
↓
生成 AOF 内容
↓
写入文件
↓
先进入操作系统文件缓存
↓
fsync
↓
真正要求操作系统同步到持久存储
如果只是调用普通文件写入,并不代表数据已经真正安全地持久化到了磁盘。
所以 AOF 提供了不同的 fsync 策略。
12. appendfsync 有哪几种?
Redis 常见有三种策略:
always
everysec
no。
13. appendfsync always
配置:
conf
appendfsync always
可以简单理解:
每批新的 AOF 写入之后都进行 fsync。
优点:
数据安全性最高。
缺点:
频繁执行 fsync,磁盘 I/O 压力大,性能影响也最大。
因此:
text
安全性
★★★★★
性能
相对最低
适合对数据丢失极其敏感的场景,但实际使用时需要接受相应的性能成本。
14. appendfsync everysec
配置:
conf
appendfsync everysec
意思是:
大约每秒执行一次 fsync。
这是 Redis 官方配置中推荐且常见的折中策略。
可以理解:
text
写命令
↓
不断追加 AOF
↓
大约每秒 fsync 一次
如果突然断电,最坏情况下可能损失最近大约一秒左右尚未同步完成的数据。
所以:
text
安全性
较高
性能
较好
这也是非常常见的配置。
15. appendfsync no
配置:
conf
appendfsync no
意思不是:
不写 AOF。
而是:
Redis 自己不主动要求每次或每秒执行 fsync,把具体什么时候真正刷盘交给操作系统。
因此:
text
写入 AOF
↓
OS 文件缓存
↓
什么时候真正同步?
↓
由操作系统决定
性能更好,但数据安全性相对更弱。
所以千万不要把:
appendfsync no
理解成:
AOF 关闭
真正控制是否开启 AOF 的是:
conf
appendonly yes
16. AOF 为什么会越来越大?
假设一个 Key:
bash
SET count 1
之后执行:
bash
INCR count
INCR count
INCR count
INCR count
最终只是:
text
count = 5
但是 AOF 可能积累了很多历史操作。
再比如:
bash
SET name A
SET name B
SET name C
SET name D
最终 Redis 真正关心的只是:
text
name = D
但是历史 AOF 中保存了大量已经没有必要重新执行的操作。
时间越长:
text
写操作越多
↓
AOF 越来越大
↓
占用磁盘越来越多
↓
重启重放时间也越来越长
所以 Redis 需要:
AOF Rewrite
17. 什么是 AOF Rewrite?
AOF Rewrite 的核心不是:
把旧 AOF 文件重新复制一次。
而是:
根据当前 Redis 数据状态,生成一份更精简的持久化基础文件。
例如原 AOF:
text
SET count 1
INCR count
INCR count
INCR count
INCR count
当前状态:
text
count = 5
重写以后并不需要保留所有历史过程。
只要能够重新构造:
text
count = 5
即可。
因此:
text
旧 AOF
大量历史命令
↓ Rewrite
新持久化数据
只保留恢复当前状态真正需要的信息
AOF Rewrite 可以通过:
bash
BGREWRITEAOF
触发。
Redis 也可以根据 AOF 大小自动触发 Rewrite。
18. AOF Rewrite 会阻塞 Redis 吗?
和 RDB 类似,Redis 会通过后台子进程完成主要 Rewrite 工作。
但真实流程在 Redis 7.0 以后发生过比较重要的变化。
当前 Redis 会:
text
fork 子进程
↓
子进程生成新的 Base AOF
与此同时
父进程继续处理客户端请求
↓
新的写操作进入新的增量 AOF
当 Base 文件生成完成以后,再通过 manifest 把新的 Base 文件和增量文件组织起来。
所以现在不能只按很多老博客中的:
"Rewrite 期间新命令全部先存在一个大内存缓冲区,最后一次性追加到新 AOF"
来理解 Redis 7+ 的实现。
19. Redis 7 以后 AOF 已经不是简单一个文件了
这是比较容易被旧资料误导的地方。
Redis 7.0 开始使用:
Multi Part AOF
也就是多文件 AOF。
主要包括:
Base File
Incremental AOF File
Manifest
例如:
text
appendonlydir/
├── appendonly.aof.1.base.rdb
├── appendonly.aof.1.incr.aof
├── appendonly.aof.2.incr.aof
└── appendonly.aof.manifest
其中 Base File 表示某个时间点的基础数据状态。
Incremental AOF 保存 Base 之后继续发生的写操作。
Manifest 则记录这些文件之间的关系和加载顺序。
所以现在说:
AOF 就是一个 appendonly.aof 文件。
对于 Redis 7+ 来说已经不够准确。
20. 什么是 Redis 混合持久化?
以前单纯使用 AOF 有一个问题:
AOF 记录大量命令,文件可能比较大,恢复时重放命令也比较慢。
而 RDB:
文件紧凑,加载速度快,但是单独使用时数据可能不够新。
所以 Redis 可以让 AOF 的 Base File 使用 RDB 格式。
当前官方配置中:
conf
aof-use-rdb-preamble yes
就是默认启用这种方式,Redis 官方配置也说明 RDB 格式的 AOF Base File 更快、更高效。
于是可以理解成:
text
AOF 持久化目录
Base File
↓
RDB 格式
↓
快速保存基础数据状态
Incremental AOF
↓
记录 Base 之后的新写操作
恢复时:
text
加载 RDB 格式 Base
↓
恢复大部分数据
↓
继续重放 Incremental AOF
↓
恢复最新状态
这通常就是大家所说的:
RDB + AOF 混合持久化。
21. 混合持久化不是简单的"同时开 RDB 和 AOF"
这一点一定要区分。
Redis 本身确实支持:
text
独立 RDB 快照
+
AOF
同时开启。
但是我们经常说的:
AOF 混合持久化
更具体指的是:
AOF 的 Base 部分使用 RDB 二进制格式,后续增量部分继续使用 AOF。
也就是:
text
AOF 持久化体系
│
┌───────┴────────┐
↓ ↓
Base File Incremental File
↓ ↓
RDB Format AOF Commands
所以:
"开启 RDB + 开启 AOF"与"AOF 使用 RDB preamble/base"不是完全同一个概念。
这个区别非常容易被博客写混。
22. RDB、AOF 同时开启,重启加载谁?
如果独立 RDB 和 AOF 都开启,在正常启动恢复时,Redis 会优先使用 AOF 来重建数据。
原因是:
AOF 通常包含比最近一次 RDB 快照更完整、更新的数据变化。
Redis 官方当前文档也明确说明,同时启用两种持久化时,启动会使用 AOF 来重建数据集。
所以可以简单理解:
text
Redis Restart
↓
AOF 存在并启用
↓
优先加载 AOF
而不是:
text
先加载 RDB
再随便加载一遍 AOF
当前 Redis 7+ 的 AOF 自己又可能包含 RDB 格式 Base,因此实际结构会比老版本更复杂。
23. RDB 和 AOF 到底怎么选?
可以先看这张表:
| 对比 | RDB | AOF |
|---|---|---|
| 记录方式 | 数据快照 | 写操作日志 / Base + Increment |
| 文件大小 | 通常较小 | 通常更大 |
| 数据安全性 | 相对较低 | 通常更高 |
| 数据丢失范围 | 可能丢失两次快照之间的数据 | 取决于 appendfsync |
| 恢复速度 | 通常较快 | 通常相对慢 |
| 运行时开销 | 相对较低 | 持续写日志 |
| 适合备份 | 很适合 | 可以,但通常更复杂 |
Redis 官方同样指出:RDB 文件更紧凑、恢复通常更快;AOF 可以提供更好的持久性,但通常文件更大、资源开销也更高。
24. 什么时候只用 RDB?
如果业务可以接受:
发生极端故障时丢失最近一段时间的数据。
例如:
Redis 只是缓存,
或者数据可以从 MySQL 等数据库重新构建,
那么 RDB 就可能已经够用。
例如:
text
MySQL
↓
真正数据源
Redis
↓
缓存
Redis 崩了:
text
重启
↓
加载 RDB
↓
缺少的数据重新从数据库缓存
这种场景没必要为了 Redis 缓存追求极强持久性。
25. 什么时候更适合 AOF?
如果 Redis 中的数据:
不能轻易丢失。
例如希望即使机器异常断电,也尽量只损失极少量最近数据,那么 AOF 会更加合适。
例如使用:
conf
appendonly yes
appendfsync everysec
就在性能和数据安全性之间取得了一个比较常见的平衡。
不过:
Redis 持久化不应该被理解成完整的灾备方案。
硬盘损坏、机器丢失、误操作、机房故障等问题,仍然需要复制、异地备份等机制配合。
26. RDB 和 AOF 能一起用吗?
官方甚至建议,如果非常重视数据安全,可以同时考虑两种持久化方式。
因为两者互相补充:
text
RDB
↓
文件紧凑
恢复快
适合备份
AOF
↓
数据更新
持久性更强
所以:
text
RDB
+
AOF
能够同时获得:
快照备份能力 + 更强的数据持久性。
27. Redis 持久化是不是越强越好?
持久性越强,往往意味着:
更多磁盘 I/O 和更高延迟。
例如:
conf
appendfsync always
数据更安全,但是频繁 fsync 会明显增加磁盘压力。
而:
conf
appendfsync no
性能更好,但是数据安全性更弱。
所以 Redis 持久化本质上也是一个取舍:
text
性能
↑
│
│
└────────→ 数据安全性
实际选择要回答一个问题:
业务到底能够接受丢多少数据?
如果:
一条都不能丢
那么 Redis 单机持久化本身可能都不是完整答案,还需要复制、集群、备份甚至其他数据库系统共同保证。
28. 最容易搞错的几个地方
第一,RDB 不是实时保存每一次写操作,它保存的是某个时间点的数据快照。
第二,AOF 不是简单"每条命令立刻写入物理磁盘"。真正的数据安全程度取决于 appendfsync。
第三,appendfsync no 不代表关闭 AOF,它只是把何时真正同步磁盘主要交给操作系统。
第四,AOF Rewrite 不是把旧 AOF 原封不动压缩,而是根据当前数据状态重新生成更精简的持久化基础。
第五,Redis 7+ 已经使用 Multi Part AOF,不能再简单认为 AOF 永远只有一个 appendonly.aof 文件。
第六,所谓"混合持久化"通常指 AOF 的 Base File 使用 RDB 格式,后续变化用 Incremental AOF 保存,并不等于简单把独立 dump.rdb 和 AOF 两种机制混为一个概念。
第七,RDB 和 AOF 可以同时开启;如果两者都启用,Redis 重启时通常使用 AOF 恢复,因为它的数据通常更加完整。