Redis 持久化怎么做?RDB、AOF 与混合持久化

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 恢复,因为它的数据通常更加完整。

相关推荐
j7~1 小时前
【C++ 标准项目】发布订阅消息队列(篇四):sqlite 与 gtest 断言框架介绍及实战应用
数据库·sqlite·gtest·断言·事件机制·tset宏
于指尖飞舞1 小时前
mysql和redis面试题总结
数据库·redis·mysql
维克兜率天1 小时前
【维克】通道突破:画出趋势的“轨道线“
前端·数据库·人工智能
ao-weilai1 小时前
MySQL数据库:复合查询
android·数据库·mysql
Omics Pro1 小时前
北理工李荣华:AI虚拟细胞证据多模态推理基准
数据库·人工智能·算法·机器学习·自然语言处理
一个天蝎座 白勺 程序猿1 小时前
IoTDB集群扩容:从慌乱到从容的经验分享
数据库·wpf·时序数据库·iotdb
写后端的胖头鱼1 小时前
详解索引下推
数据库·索引失效·索引·icp·索引下推
要开心吖ZSH2 小时前
系统并发与 QPS 上限:从 200 个线程到每秒几万请求
redis·mysql·tomcat·并发·连接池·qps