Redis 数据持久化策略和原理机制
Redis 提供了两种主要的持久化机制,4.0 之后还引入了 混合持久化(RDB + AOF) 的方式,结合两者优点:
- RDB机制(Redis DataBase)(快照二进制)在指定时间间隔快照存储,将某一时刻的内存快照数据以二进制方式写入磁盘。
- AOF机制(Append Only File)(命令追加)记录每次对服务器写操作,将所有的操作命令按顺序记录下来,追加到特殊的文件中。当服务器重启的时候会重新执行这些命令来恢复原始的数据,以redis协议追加保存每次写的操作到文件末尾,Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.
- 混合机制: (同时开启两种持久化方式)将当前的数据以RDB方式写入文件,再将操作命令以AOF方式写入文件。 在这种情况下, 当redis重启的时候会优先载入AOF文件来恢复原始数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.
RDB(Redis DataBase)
RDB 持久化是指在指定的时间间隔内,将内存中的数据集快照写入磁盘。创建一个经过压缩的二进制文件(默认 dump.rdb)。
RDB 机制会生成全量rdb文件,可通过配置修改文件名和目录。
优缺点和适用场景
优点:
高性能:对 Redis 主进程影响极小。父进程除了 fork 子进程时有短暂阻塞外,其余时间可以继续处理请求。适合大规模数据恢复和备份。
紧凑的文件:RDB 文件是压缩的二进制格式,文件体积小,非常适合用于灾难恢复、备份和不同环境之间的数据迁移。
快速恢复:在数据量很大时,RDB 的恢复速度远快于 AOF。
缺点:
-
数据安全性低:RDB 是定时快照,在两次快照之间如果服务器宕机,会丢失最后一次快照之后的所有数据。
在redis意外停止工作的情况下可能丢失数据,虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据.
-
fork 可能阻塞:虽然 fork 操作本身很快,但如果数据量非常大,fork 过程可能会消耗较多 CPU,并且在内存写入压力大时,可能导致父进程短暂阻塞。在极端情况下,如果数据集巨大,fork 操作本身可能会很慢。
RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候**,fork的过程是非常耗时**,可能会导致Redis在一些毫秒级内不能响应客户端的请求.如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度.
适用场景:
-
适合备份,RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集.
-
RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3(可能加密),非常适用于灾难恢复.
-
RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.
-
与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些.
快照原理
在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

Redis 借助了操作系统提供的写时复制技术(Copy-On-Write, COW),可以让在执行快照的同时,正常处理写操作。
Linux中fork()是使用写时复制的页实现的,所以它唯一的代价是:复制父进程的页表以及为子进程创建任务结构所需的时间和内存。
具体流程:
-
触发机制:当满足配置条件(如
save 900 1表示900秒内至少1个键变化)或手动执行SAVE/BGSAVE命令时,持久化开始。 -
父进程 fork 子进程:Redis 主进程(父进程)会 fork 一个子进程。这个子进程会拥有与父进程完全相同的内存数据副本。
-
子进程负责写入:子进程将内存中的数据写入一个临时的 RDB 文件。由于是副本,子进程的写入操作不会影响父进程继续处理客户端的命令。
-
替换旧文件:当子进程完成对新 RDB 文件的写入后,它会用新文件原子地替换旧的 RDB 文件。
简单来说就是
fork()函数会复制父进程的地址空间到子进程中,复制的是指针,而不是数据,所以速度很快。当没有发生写的时候,子进程和父进程指向地址是一样的,父子进程共享内存空间。直到发生写的时候,系统才会真正拷贝出一块新的内存区域,读操作和写操作在不同的内存空间,子进程所见到的最初资源仍然保持不变,从而实现父子进程隔离。
此做法的主要优点是如果期间没有写操作,就不会有副本被创建。
Redis 可以高效地执行 RDB 持久化操作,并且不会对 Redis 运行过程中的性能造成太大的影响。同时,这种方式也提供了一种简单有效的机制来保护 Redis 数据的一致性和可靠性。
![]()
写时复制
写时复制 (Copy-On-Write, COW)
在 fork 子进程的瞬间,父子进程共享同一片内存数据。当父进程要修改某一片数据时(比如执行了一个 SET 命令),操作系统会将被修改的内存页复制一份,确保子进程的数据不受影响。这使得 fork 操作非常快速,并且大部分情况下内存消耗不会翻倍。
拓展:JDK的
CopyOnWriteArrayList和CopyOnWriteArraySet容器也使用到了写时复制技术。
主动触发
**主动方式:**即手动触发,涉及两个操作命令:
-
SAVE:阻塞 Redis 直到 RDB 完成(不推荐生产使用)。 -
BGSAVE:利用COW技术,创建子进程,由子进程负责RDB过程,主进程可以继续处理其他命令,后台异步执行(推荐)。
自动触发
自动方式:由配置文件来完成,自动触发 BGSAVE命令
在redis.conf配置文件中有如下配置:
1 | save 900 1 # 900秒内至少有1个key发生变化 |
其他触发
-
执行
FLUSHALL(会生成空 RDB 文件) -
主从复制时,主节点自动执行 BGSAVE
-
关闭 Redis 时(若配置了
save)
RDB 阻塞
redis提供了两个命令来生成RDB文件,分别是save和bgsave。
-
save:在主线程中执行,会导致阻塞。
-
bgsave:创建一个子进程,专门用于写入RDB文件,避免了主线程的阻塞,这也是Redis RDB文件生成的默认配置。
子进程需要通过fork操作,从主线程中创建出来。子进程在创建后不会再阻塞主线程,但是,fork这个创建过程本身会阻塞主线程,而且主线程内存越大,阻塞时间越长。
RDB 每次在 fork 子进程来执行 RDB 快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,或者甚至数秒。
持久化配置
Redis会将数据集的快照dump到dump.rdb文件中。
通过配置文件来修改Redis服务器dump快照的频率和位置:
1 | save 900 1 #在900秒(15分钟)内,如果至少有1个key发生变化,则dump内存快照。 |
AOF(Append Only File)
AOF 持久化是以日志的形式记录每一个写操作命令。当 Redis 重启时,会重新执行 AOF 文件中的所有命令来重建内存中的数据。
A每一个写、删除操作,查询操作不会记录,以文本的方式追加记录
优缺点和适用场景
优点:
-
数据安全高:根据
appendfsync策略,可以提供非常好的持久化保证,最多丢失一秒数据。使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据
-
易于理解和解析:AOF 文件是纯文本协议格式,易于阅读和手动修复(不推荐)。
-
容灾性好:即使 AOF 文件末尾有损坏(比如突然断电),可以使用
redis-check-aof工具轻松修复。
缺点:
-
文件体积大:通常 AOF 文件会比同数据集的 RDB 文件大。
-
恢复速度慢:在数据集很大时,重新执行所有 AOF 命令来恢复数据会比加载 RDB 文件慢。
在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。
-
对性能影响相对较大:虽然
everysec策略性能不错,但在写入负载极高的场景下,AOF 仍可能成为瓶颈。
适用场景:
-
占用内存少,AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题.
-
重写压缩文件,Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
-
**容易阅读,**AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。
追加原理
AOF 的工作流程可以概括为:命令追加、文件写入、文件同步。
-
命令记录:修改和新增命令执行后,都会以 Redis 协议格式追加到服务器内部的 AOF 缓冲区。
-
写入与同步策略:根据配置的
appendfsync策略,决定何时将缓冲区的内容写入并同步到 AOF 磁盘文件。always:每次写命令都立即同步到磁盘。数据最安全,不会丢失任何命令,但性能最差。everysec:每秒同步一次。这是默认推荐的策略,是一种折中方案。在性能和数据安全之间取得了很好的平衡,最多丢失1秒钟的数据。no:由操作系统决定何时同步。性能最好,但数据丢失风险最高。
重写机制
随着命令不断写入,AOF 文件会越来越大。为了解决这个问题,Redis 提供了 AOF 重写 机制。
-
目的:创建一个新的、更小的 AOF 文件,这个文件包含了恢复当前数据集所需的最小命令集合。例如,对一个键进行了100次
INCR,重写后只需要一条SET命令。 -
过程:与 RDB 类似,也是 fork 一个子进程 在后台完成。子进程遍历数据库,将每个键值对用一条命令记录到新的 AOF 文件中。在重写期间,新的写命令会被同时记录到原有的 AOF 缓冲区和重写 AOF 缓冲区。当子进程完成重写后,会通知父进程,父进程会将重写 AOF 缓冲区的内容追加到新的 AOF 文件中,然后原子地替换旧的 AOF 文件。
重写缓冲区
子进程在AOF重写期间,父进程还是在继续接收和处理命令。
-
子进程:拥有 fork 创建时刻的内存数据副本,它基于这个"过去"的状态来生成新的 AOF 文件
-
父进程:继续正常服务,接收并执行新的写命令,数据库状态在不断变化
新的命令可能会对现有的数据库状态进行修改,从而使得服务器当前的数据库状态和重写后的AOF文件所保存的数据库状态不一致。
为了解决这种数据不一致问题,Redis服务器设置了一个AOF重写缓冲区(aof_rewrite_buf_blocks)。
AOF重写缓存区在AOF重写时开始启用。
执行步骤:
-
准备阶段,当开始执行
BGREWRITEAOF或满足自动重写条件时:- 父进程 fork 出子进程
- 父进程会同时开启 AOF 重写缓冲区
-
重写期间的写命令处理
从这一刻开始,所有到达的写命令会同时进入两个缓冲区:
缓冲区 用途 消费者 常规 AOF 缓冲区 用于正常的 AOF 持久化 AOF 文件(根据 appendfsync策略)AOF 重写缓冲区 专门用于保证重写一致性 新的 AOF 文件(在重写完成后) 1
2
3
4
5
6
7
8
9def handle_write_command(command):
# 1. 执行命令,更新内存数据库
execute_command(command)
# 2. 将命令追加到常规 AOF 缓冲区
aof_buf.append(command)
# 3. 同时追加到 AOF 重写缓冲区
aof_rewrite_buf.append(command) -
子进程完成重写
- 子进程向父进程发送信号
- 父进程收到信号后,将 AOF 重写缓冲区中的所有命令 追加到子进程生成的新 AOF 文件中
- 原子性地用新的 AOF 文件替换旧的 AOF 文件,这样重写后数据库状态就和服务器当前的数据库状态一致
AOF重写缓存区同步至AOF文件中,这个过程是同步的,会阻塞父进程,在其他时候,AOF后台重写都不会阻塞父进程。
AOF 阻塞
AOF日志是在主线程中执行的,采用先存数据再写日志的方式,可以避免对当前命令的阻塞,但可能会给下一个操作带来阻塞风险。这是因为如果把日志文件写入磁盘时,磁盘写压力大,就会导致写盘很慢,进而阻塞后续操作。
Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;
Everysec,每秒写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,每隔一秒把缓冲区内容写入磁盘。
No,操作系统控制写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。
持久化配置
在Redis的配置文件中存在三种同步方式,通过 appendfsync 配置控制写入磁盘的频率::
1 | # 每个命令都同步(最安全,性能最差) |
混合持久化(RDB + AOF)
为了解决 AOF 恢复慢的问题,同时保证数据安全,Redis 4.0 引入了混合持久化。
当开启混合持久化(aof-use-rdb-preamble yes)后,AOF 重写过程会发生变化,AOF 重写时不再使用纯命令日志,而是先写入 RDB 格式的全量数据,再追加重写期间的增量命令:
子进程进行重写时,不再是直接将数据以命令形式写入新 AOF 文件。
而是会先将当前内存中的数据做一次 RDB 快照,并写入新 AOF 文件的开头。
然后,在重写期间接收到的写命令,会以 AOF 格式(Redis 协议)追加到 RDB 数据之后。
“先拍一张快照,然后记录之后的指令”
最终生成的新的 AOF 文件是一个“RDB头部 + AOF尾部”的混合体。半部分是 RDB 二进制格式,后半部分是 AOF 命令文本。
-
结合了 RDB 和 AOF 的优点:
- 快速恢复:重启时,先加载 RDB 部分的内容,速度非常快,再重放少量 AOF 命令(精确)。文件大小比纯 AOF 小,恢复速度比纯 AOF 快。
- 数据安全:再重放尾部 AOF 格式的增量命令,保证数据不丢失。
1 | # 开启 AOF |
比较和建议
如果两个都配了优先加载AOF
-
RDB 像是一次全量拍照,速度快,文件小,但可能会丢数据。
-
AOF 像是记录所有操作指令的日志,数据安全,但文件大,恢复慢。
-
AOF比RDB更安全也更大
-
RDB性能比AOF好
| 特性 | RDB | AOF | 混合持久化 (推荐) |
|---|---|---|---|
| 数据安全 | 低,可能丢失分钟级数据 | 高,最多丢失秒级数据 | 高,同 AOF |
| 文件大小 | 小,压缩二进制 | 大,文本协议 | 比纯 AOF 小 |
| 恢复速度 | 快 | 慢 | 非常快 (先RDB,后增量AOF) |
| 对性能影响 | fork 时可能阻塞,写入压力小 | 写入压力取决于 appendfsync |
同 AOF |
| 适用场景 | 容灾备份,允许分钟级数据丢失 | 对数据安全要求高,如金融业务 | 绝大多数生产环境 |
如何选择持久化策略?
| 场景 | 推荐方案 |
|---|---|
| 要求高性能,可容忍少量数据丢失 | 仅 RDB |
| 要求高数据安全性,不能容忍丢失 | AOF(everysec或always) |
| 既要快速恢复,又要高安全性 | 混合持久化(推荐) |
| 仅用作缓存,无需持久化 | 关闭 RDB 和 AOF |