Redis 持久化
Redis 本身是缓存,为什么它的持久化会被如此重视呢?
如何确保内存中的数据不因突发事件而丢失?如何在确保性能的前提下,为数据提供一个更加稳固的避风港?这是否矛盾
现在的情况差不多是这样,Redis 早期是纯缓存,但随着功能迭代,很多场景下它被当作内存数据库使用(如存储用户会话、购物车、实时排行榜)。此时持久化的价值体现在两点:
本质是因为它早已超越纯缓存,承担了数据库的职能,数据丢失会直接影响业务
Redis 提供三种持久化的方式: 分别是 RDB(Redis Database Snapshot) 和 AOF(Append Only File)以及 混合持久化。
RDB持久化
介绍RDB
既然我的数据都在内存中存放着,最简单的就是遍历一遍把它们全都写入文件中。
不过我的数据量有点大,要是全部备份一次得花不少时间,所以不能太频繁的去做这事,要不然我不用干正事了,光花时间去备份了。所以我要设置一些触发条件。
Redis 决定提供一个配置参数,既可以支持周期性备份,也可以避免做无用功。
RDB(Redis Database)是通过周期性生成内存数据的完整快照文件来实现持久化,核心是 “定时备份”。
将当前内存中的数据快照(snapshot)保存到硬盘的过程。换句话说,Redis 会创建一个代表某一时刻的数据集的磁盘文件。
类似相机的快门点击。每当你点击快门,你都会捕捉到那个特定时刻的场景。RDB的工作方式很相似,只不过它捕捉的是数据的状态。
- Redis 会在满足预设条件(如 “1 小时内修改 1000 次”)时,fork 出一个子进程。
- 子进程读取内存数据,生成一个压缩的二进制文件(如
dump.rdb),写入磁盘。 - 主进程不参与文件写入,仅继续处理客户端请求,几乎不影响性能。
RDB的持久化流程
整个生成流程图如下

流程说明
首先,redis需要被触发RDB生成才能开始RDB持久化,触发 RDB 文件的生成有以下两种方式:
手动触发:通过执行 SAVE 或 BGSAVE 命令。
自动触发:基于 Redis 配置文件中的 save 指令设置的条件。(默认是通过 BGSAVE 命令来触发的)
例如:
1
2
3
4redis 配置文件 save 指令设置:
save 3600 1 # 3600秒内如果超过1个key被修改则生成 RDB
save 300 100 # 300秒内如果超过100个key被修改则生成 RDB
save 60 10000 # 60秒内如果超过10000个key被修改则生成 RDB
创建 fork 子进程
当执行 BGSAVE 命令时,Redis 主进程(父进程)会执行 fork 操作来创建一个子进程。这是一个昂贵的操作,尤其当数据集很大时。但好处是,一旦 fork 完成,父进程可以立即返回处理其他客户端请求,而不需要等待 RDB 的生成过程。
这里涉及到操作系统级别的知识。当 fork 操作执行后,子进程会获得父进程内存中的数据副本。但由于操作系统使用写时复制(Copy-On-Write, COW)技术,fork 时不复制数据,只共享数据;只有当数据被修改时,才复制被修改的部分。任何在父进程(Redis主进程)上发生的写操作不会影响子进程中的数据。这确保了子进程中的数据是隔离的,不受父进程中数据更改的影响。
- 假设没有 COW 技术,当主进程 fork 子进程时,操作系统会完整复制主进程的所有内存数据给子进程。比如 Redis 使用了 10GB 内存,fork 时就需要额外 10GB 内存来存储副本。这会导致瞬间爆炸,所以只把被修改的数据复制,未修改的直接复用共享,这样 fork 操作本身几乎不耗时,主进程才能快速回到处理请求的状态。
子进程生成RDB文件:
子进程将开始遍历整个数据集,将所有的数据写入一个新的临时RDB文件。这是一个纯I/O操作,并且是线性的,所以非常快。子进程不需要处理任何客户端请求,只专注于写 RDB 文件,所以效率很高。
RDB文件替换:
一旦子进程完成了新的 RDB 文件的写入,它会替换掉旧的 RDB 文件,并发送一个信号通知父进程任务完成。然后子进程退出。
RDB 不是最好的持久化策略,它带来的问题比较明显
| 优点 | 缺点 |
|---|---|
| 快照文件体积小,恢复速度极快(适合大规模数据) | 备份有 “时间间隔”,两次快照间的数据可能丢失(如 1 小时备份一次,崩溃时会丢 1 小时数据) |
| 生成快照时,主进程无 IO 操作,对性能影响小 | 当内存数据量大时,fork 子进程会消耗额外内存(需复制主进程地址空间),可能短暂阻塞 |
理解 RDB 的本质后,如何生成这个快照呢?使用 SAVE 和 BGSAVE 命令即可。
除了自动触发,开发中常需要手动生成快照(如部署前备份、数据迁移),两种命令的区别需注意:
| 命令 | 行为 | 适用场景 |
|---|---|---|
SAVE |
主进程直接生成 RDB,期间阻塞所有客户端请求(直到完成) | 紧急备份,且能接受短暂阻塞 |
BGSAVE |
fork 子进程生成 RDB,主进程不阻塞(依赖 COW 机制) | 日常手动备份,不希望影响服务 |
SAVE在数据量大时可能导致 Redis 长时间无响应(如 10GB
数据可能阻塞几秒到几十秒),生产环境慎用,优先用BGSAVE。
RDB核心配置(redis.conf中
RDB 相关配置)
Redis 默认是不会开启持久化选项的,只要重启 redis,redis 之前保存的数据都会丢失的。
因此在实际的生产环境中,我们都会配置 Redis 的 RDB 配置。
在实际开发中,RDB 的配置需要结合业务对 “数据安全性” 和 “性能损耗” 的容忍度来调整
快照触发规则(
save指令)RDB 的核心是 “满足特定条件时自动生成快照”,通过
save <seconds> <changes>定义,意思是 “在<seconds>秒内发生<changes>次数据修改,则触发 BGSAVE”。如果想禁用 RDB 持久化,可以在配置文件中注释掉所有的 save 指令,并且重启 Redis 服务即可。默认配置
1
2
3save 900 1 # 900秒(15分钟)内有1次修改
save 300 10 # 300秒(5分钟)内有10次修改
save 60 10000 # 60秒(1分钟)内有10000次修改RDB 文件路径与名称
dir ./:定义 RDB 文件的存储目录(默认当前目录,建议改为绝对路径,如dir /var/lib/redis,避免进程切换目录后找不到文件)。- 目录需确保 Redis
进程有读写权限(避免
Permission denied错误)。而且建议单独挂载磁盘存储 RDB 文件,避免与系统盘共享,防止 IO 竞争
- 目录需确保 Redis
进程有读写权限(避免
dbfilename dump.rdb:定义 RDB 文件名(可自定义,如dbfilename redis_6379.rdb,方便区分不同实例)。
压缩与校验
rdbcompression yes:是否压缩 RDB 文件(默认yes)。rdbcompression 设置为 yes 时,表示启用 rdbcompression, 启用 rdbcompression 会使Redis在保存数据前先对其进行压缩,这样可以减少存储空间的使用(尤其字符串多)。但是压缩会消耗 CPU,节省磁盘空间。
若 CPU 资源紧张(如高频写入场景),可设为
no,以 CPU 换 IO 性能。
rdbchecksum yes:是否对 RDB 文件进行校验(默认yes)。- 校验会在生成和加载 RDB 时增加少量 CPU 开销,但能检测文件损坏(如磁盘故障导致的文件不完整)。
- 若对性能极致敏感且能接受极小的文件损坏风险,可设为
no。
错误处理(
stop-writes-on-bgsave-error)1
stop-writes-on-bgsave-error yes
- 含义:当此选项被设置为 yes 时,如果 BGSAVE 失败(如磁盘满、权限不足),Redis 将停止所有写入操作。这是一种保护机制,确保在可能的磁盘问题或其他故障时,不再接受可能导致数据丢失的写操作。
- 我们通常保持
yes(默认),避免在持久化失败时继续写入数据,导致数据丢失风险扩大(此时客户端会收到(error) MISCONF Redis is configured to save RDB snapshots...错误,需人工介入处理)。
数据安全性校验(sanitize-dump-payload)
当你有一个Redis数据备份(RDB文件)或者使用 RESTORE 命令来导入数据时,你会希望这些数据是安全的,没有任何问题。但是,有些时候数据可能存在问题,这可能会导致 Redis 在后续的操作中崩溃或者出现错误。
sanitize-dump-payload 这个配置项就是帮助你在加载数据时检查这些数据的安全性。
- 如果你设置为no,那么Redis不会检查数据,这样会更快,但是风险也更大。
- 如果设置为yes,Redis会检查所有的数据,确保它们是安全的,不会导致问题。这样更安全,但可能会稍微慢一些。
- 如果设置为clients,只有从客户端(比如你的应用程序)发来的数据会被检查,而从其它 Redis 实例同步来的数据或RDB文件不会被检查。
主从复制后是否删除(rdb-del-sync-files )
在 Redis 的主从复制过程中,主节点需要生成 RDB 文件并将其发送给从节点,帮助从节点进行数据同步。在某些特定的安全环境中,一旦复制完毕,可能会要求尽快删除这些RDB文件。
rdb-del-sync-files 这个选项当设置为 yes 时,并且主节点没有启用 RDB 和 AOF持久化,redis 会自动删除这些与复制相关的 RDB 文件。
默认为no,这意味着与复制相关的RDB文件在同步后不会被自动删除。
而且Redis 启动时会自动加载 RDB 文件恢复数据,无需额外配置,但要注意:
- 启动时 Redis
会检查
dir目录下是否存在dbfilename指定的 RDB 文件,若有则加载。 - 加载过程中 Redis 会阻塞客户端请求(直到加载完成),因此大文件恢复可能导致服务启动慢(需提前评估,避免高峰期重启)。
- 若同时开启 AOF(
appendonly yes),Redis 会优先加载 AOF 文件(因 AOF 数据更完整),而非 RDB。
AOF持久化
介绍AOF
你看啊,你这个 RDB 周期性去备份,周期还是分钟级别的,你可知道咱们这服务每秒钟都要响应多少请求,像你这样不得丢失多少数据?所以说,redis 受到了 mysql 的二进制日志binlog的影响,产生了 AOF 持久化。
AOF(Append Only File)是通过实时记录所有写操作指令(如 SET、HSET)来实现持久化,核心是 “记录过程”。
工作原理如下
- 客户端执行写操作时,Redis 会将指令追加到 AOF 缓冲区(而非直接写磁盘)。
- 缓冲区根据 “刷盘策略”
定期写入磁盘文件(如
appendonly.aof),确保指令不丢失。 - 恢复数据时,Redis 会重新执行 AOF 文件中的所有指令,重建内存数据。
不过还是遇到了RDB方案同样的问题,我该多久写一次文件呢?肯定不能每执行一条写入命令就记录到文件中,那会严重拖垮我的性能!我决定准备一个缓冲区,然后把要记录的命令先临时保存在这里,然后再择机写入文件,我把这个临时缓冲区叫做 aof_buf。
但是操作系统自己也有个缓冲区,redis写的数据被他缓存起来了,没有给我写入到文件中去
所以 AOF 还涉及到一个刷盘策略(文件同步策略),刷盘策略决定了 “指令从缓冲区到磁盘的频率”,直接影响数据安全性和性能:
- always:每次写操作都刷盘。数据绝对安全,但频繁 IO 会严重拖慢性能,适合对数据安全性要求极高的场景。
- everysec:每秒刷盘一次。最多丢 1 秒数据,性能损耗适中,是默认且最常用的策略。
- no:由操作系统决定刷盘时机。性能最好,但数据丢失风险最高(操作系统崩溃可能丢大量数据)。
同样,AOF也是问题比较明显的持久化策略
| 优点 | 缺点 |
|---|---|
| 数据安全性高,可通过刷盘策略控制丢失范围(最小丢 1 秒) | AOF 文件体积会持续增大(如反复修改同一 key),需定期 “重写” 优化 |
| 指令记录完整,恢复时数据更精准 | 恢复速度慢(需重新执行所有指令),尤其文件体积大时 |
AOF 重写
AOF 文件涉及到载入的问题,当 Redis 服务器启动时,如果配置为使用 AOF 持久化,它会检查 AOF 文件的存在。如果找到 AOF 文件,Redis 会加载并执行其中的命令来恢复数据
假如 Redis 的 RDB 和 AOF 持久化都启用,redis 在载入数据的时候,是载入 AOF 文件?还是 RDB 文件?
Redis 会优先载入 AOF 文件来恢复数据,而不是 RDB 文件。这是因为 AOF 文件通常包含了更完整的操作记录,从而能够恢复更完整的数据状态。而 RDB 文件是定时生成的数据快照,所以它可能没有记录到最后一次快照之后发生的所有更改。因此,使用 AOF 文件恢复数据可以提供更高的数据完整性。
上面说了,AOF 通过追加写操作命令到文件的方式来恢复数据的,会带来一个问题,就是 AOF 文件体积会越来越大
面对这样的问题,Redis 提供了一个非常有效的优化手段,那就是 AOF 重写。
AOF 重写,可以看作是对 AOF文件 进行的一次“精简”操作。它的目的是减少AOF文件的大小,并去除那些冗余的、不再必要的命令,使得该文件只包含恢复当前数据集所需的最小命令集。
这样就能节省磁盘空间的同时,加速恢复速度
AOF 重写的流程比较复杂,有这样一张图参考
我们来根据这张图说一下 AOF 如何重写的
AOF 重写条件的触发
和 RDB 一样,AOF 重写也要一个机会来触发,AOF 文件会因持续记录写操作而不断增大,重写的目的是压缩 AOF 文件体积(去除冗余指令,如多次修改同一 key 的无效操作)。触发条件由两个配置项控制:
auto-aof-rewrite-percentage 100:当 AOF 文件大小比 “上次重写后的大小” 增长超过 100%(即翻倍)时,触发重写。auto-aof-rewrite-min-size 64mb:AOF 文件的最小体积阈值(默认 64MB),即使增长比例达标,若文件未到 64MB 也不会触发重写。
若上次重写后 AOF 文件是 50MB,当文件增长到 100MB(增长 100%)且超过 64MB 时,触发自动重写;若文件只有 30MB,即使增长到 60MB(增长 100%),因未达 64MB 的最小阈值,也不会触发。
AOF 重写的执行流程
流程图完整呈现了重写的四个关键阶段,每个阶段都体现了 “性能与数据安全的平衡”:
- boss1阶段:检测与决策(流程图 “检测 AOF
文件大小→达到重写条件?”)
- Redis 周期性(内部定时任务)检查 AOF 文件大小,判断是否满足 “增长比例 + 最小体积” 的重写条件。
- 若不满足,继续正常处理客户端请求;若满足,进入fork 进程阶段。
- boss2阶段:fork 子进程与双缓冲区机制(流程图 “fork
进程→主进程双写 + 子进程重写”)
- fork 子进程:主进程 fork 出一个子进程专门负责 AOF 重写(类似 RDB 的 BGSAVE,依赖写时复制技术,主进程不阻塞)。
- 主进程双缓冲区:
- 新的写操作命令会同时写入
aof_buf(正常 AOF 缓冲区,用于实时刷盘)和AOF重写缓冲区(记录 fork 后新产生的写操作)。避免出现子进程在重写期间,我要是修改了数据,就会出现和重写的内容不一致的情况 - 这样既保证了 “当前写操作的持久化不中断”,又为 “重写后的 AOF 文件补全新操作” 做准备。
- 新的写操作命令会同时写入
- 子进程重写:子进程读取 fork
时刻的内存数据,将其转换为
“最小化的有效指令”(如将
SET key 1 → SET key 2 → SET key 3合并为SET key 3),生成新的 AOF 文件。
- boss3阶段:合并新操作(流程图 “主进程将 AOF
重写缓冲区写入新文件”)
- 子进程完成重写后退出,主进程将
AOF重写缓冲区中记录的 “fork 后新写操作” 追加到新生成的 AOF 文件中,确保数据不丢失。
- 子进程完成重写后退出,主进程将
- boss4阶段:替换旧文件(流程图 “替换旧的 AOF 文件”)
- 新 AOF 文件生成并补全所有操作后,Redis 用新文件替换旧 AOF 文件,后续写操作直接写入新文件,重写流程结束。
- boss1阶段:检测与决策(流程图 “检测 AOF
文件大小→达到重写条件?”)
说一嘴神秘小机制,重写时会过滤无效指令,只保留
“能还原最终数据的最小指令集”,大幅减小文件体积。而且重写过程中所有写操作都有冗余记录(aof_buf和重写缓冲区)也导致不会因重写导致数据丢失,安全这一块。
1 | 旧AOF指令:SET user:1 name "Alice" → SET user:1 age 20 → SET user:1 name "Bob" |
可通过BGREWRITEAOF命令手动触发重写(类似BGSAVE,主进程不阻塞),常用于
“提前压缩 AOF 文件” 或 “测试重写逻辑”。
AOF 核心配置
AOF 开关与基础路径配置
appendonly1
appendonly no # 默认关闭,改为yes开启AOF持久化
- 作用:控制是否开启 AOF 持久化。
- 建议:若需保证数据低丢失(如核心业务数据),必须设为
yes;纯缓存场景可保持no。
appendfilename1
appendfilename "appendonly.aof"
- 作用:定义 AOF 文件的名称(默认
appendonly.aof)。 - 建议:可自定义名称(如
appendonly_6379.aof),方便多实例部署时区分文件。
- 作用:定义 AOF 文件的名称(默认
dir1
dir ./ # AOF文件的存储目录,与RDB共享
- 作用:指定 AOF 文件的存储路径(与 RDB 目录相同)。
刷盘策略(核心:平衡数据安全与性能)
AOF 通过 “将写操作指令写入缓冲区后刷盘”
实现持久化,刷盘策略由appendfsync控制,这是 AOF
配置中最关键的参数:
appendfsync1
appendfsync everysec # 默认策略,每秒刷盘一次
- 可选值及对比参考刷盘策略
AOF 重写配置(解决文件膨胀问题)
auto-aof-rewrite-percentage
1 | auto-aof-rewrite-percentage 100 # 增长比例阈值,默认100%(即翻倍) |
- 作用:当 AOF 文件大小比 “上次重写后的大小” 增长超过该比例时,触发重写。
- 调优:
- 写操作频繁的场景,可提高该值(如
200),减少重写频率,降低 fork 开销。 - 写操作稀疏的场景,可降低该值(如
50),让文件更早压缩。
- 写操作频繁的场景,可提高该值(如
auto-aof-rewrite-min-size
1 | auto-aof-rewrite-min-size 64mb # 最小文件体积阈值,默认64MB |
- 作用:即使增长比例达标,若文件未达到该体积,也不会触发重写。
- 调优:
- 小数据量场景(如 AOF 文件长期低于
64MB),可降低该值(如
32mb),确保文件能被压缩。 - 磁盘空间充足时,可提高该值(如
128mb),减少重写次数。
- 小数据量场景(如 AOF 文件长期低于
64MB),可降低该值(如
aof-rewrite-incremental-fsync
1 | aof-rewrite-incremental-fsync yes # 重写时是否增量刷盘,默认开启 |
- 作用:在 AOF 重写过程中,子进程生成新文件时,每写入 32MB 数据就刷盘一次。
- 优势:避免重写时因大量数据写入导致的 “磁盘 IO 突增” 或 “文件未刷盘导致的丢失风险”。
- 实际建议:保持
yes,这是 Redis 2.4 + 的优化策略,无需修改。
错误处理与兼容性配置
no-appendfsync-on-rewrite
1 | no-appendfsync-on-rewrite no # 默认关闭 |
- 作用:当 AOF
重写时,是否暂停
appendfsync的刷盘操作(避免重写和刷盘的 IO 竞争)。 - 影响:
- 设为
yes:重写时刷盘暂停,可能导致最多 “刷盘间隔” 的数据丢失(如everysec策略下丢 1 秒数据),但重写速度更快。 - 设为
no:重写时刷盘正常,数据更安全,但 IO 压力大,可能拖慢重写速度。
- 设为
- 建议:核心业务设为
no(优先保证数据安全);非核心业务可设为yes(优先保证重写效率)。
aof-load-truncated1
aof-load-truncated yes # 加载AOF时,若文件末尾不完整是否继续加载,默认开启
- 作用:若 AOF 文件因异常(如断电)导致末尾指令不完整,Redis 是否尝试加载已有的完整部分。
- 建议:保持
yes,避免因文件轻微损坏导致整个 AOF 无法加载。
混合持久化
这个混合是怎么个混合
我们已经探讨了 Redis 的 RDB 和 AOF 持久化。RDB 提供快速的数据恢复,但可能有数据丢失风险;AOF 保证了数据完整性,但文件可能过大,恢复速度较慢。
那么,是否有一种既快速又可靠的方法?需要我们祭出 Redis 的混合持久化策略
混合持久化是 Redis 4.0 新引入的持久化策略,结合了 RDB 的快速恢复和 AOF 的数据完整性的优点,它首先以 RDB 格式保存当前数据状态,然后继续以 AOF 格式记录新的写操作,确保数据完整性并优化恢复速度。
也就是说,Redis 还可以同时使用 AOF 持久化和 RDB 持久化。 在这种情况下,当 Redis 重启时,它会优先使用 AOF 文件来还原数据集, 因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。你甚至可以关闭持久化功能,让数据只在服务器运行时存在。
在普通 AOF 重写时,子进程生成的是 “纯 AOF 指令文件”;而混合持久化中,子进程生成的是 “RDB 快照 + 增量 AOF 指令” 的混合文件。
- 开头部分是RDB 二进制快照:恢复时加载速度极快(像纯 RDB 一样)。
- 末尾部分是增量 AOF 指令:记录 RDB 快照生成后新的写操作,保证数据完整性(像纯 AOF 一样)。
一般来说,如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
如果你非常关心你的数据,但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快, 除此之外, 使用 RDB 还可以避免之前提到的 AOF 程序的 bug 。
因为以上提到的种种原因, 未来 redis 可能会将 AOF 和 RDB 整合成单个持久化模型。 (这是一个长期计划。)
混合持久化的工作流程
在 AOF 重写之前,RDB 和 AOF 都是按照它们各自的持久化策略工作的。当 AOF 重写被触发时,混合持久化才开始发挥作用:将当前的数据集会首先以RDB 格式写入新 AOF 文件的顶部,然后再追加新的命令到文件的末尾。
流程图如下
- 首先,还是触发重写条件:
- 与普通 AOF 重写逻辑一致:检测 AOF 文件大小,判断是否满足
“增长比例(
auto-aof-rewrite-percentage)+ 最小体积(auto-aof-rewrite-min-size)” 的重写条件。
- 与普通 AOF 重写逻辑一致:检测 AOF 文件大小,判断是否满足
“增长比例(
- fork 进程与混合文件生成:
- 步骤
1(子进程):创建新文件
appendonly.rdb(注意后缀,这是混合文件的载体)。 - 步骤 2(子进程):生成RDB
快照,写入
appendonly.rdb文件开头(这是混合持久化的核心 —— 把 RDB 作为 AOF 的 “基干”)。 - 步骤
3(主进程):主进程继续处理客户端请求,新的写操作同时写入
aof_buf(普通 AOF 缓冲区)和AOF 重写缓冲区(记录 RDB 生成后的增量操作)。 - 步骤 4(主进程):子进程完成 RDB
生成后退出,主进程将 “重写缓冲区中的增量 AOF 指令”
追加到
appendonly.rdb文件末尾。
- 步骤
1(子进程):创建新文件
- 替换旧文件:
- 新的混合文件(
appendonly.rdb)生成后,替换旧的 AOF 文件,后续写操作直接写入该文件。
- 新的混合文件(
看起来没那么难,只是两种都有,取了各自相对好的策略
混合持久化的配置
开启配置(Redis 4.0 + 支持)
在redis.conf中添加:
1 | aof-use-rdb-preamble yes # 默认为yes,开启混合持久化 |
然后各自配置 aof 和 rdb 的配置







