Skip to content

Latest commit

 

History

History
608 lines (435 loc) · 34.3 KB

File metadata and controls

608 lines (435 loc) · 34.3 KB
title Redis持久化机制详解
description 深入解析Redis三种持久化机制RDB快照、AOF日志和混合持久化的工作原理、配置方法和优缺点对比,帮助你选择适合业务场景的持久化策略。
category 数据库
tag
Redis
head
meta
name content
keywords
Redis持久化,RDB,AOF,混合持久化,bgsave,数据恢复,Redis备份,fork子进程

使用缓存的时候,我们经常需要对内存中的数据进行持久化也就是将内存中的数据写入到硬盘中。大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了做数据同步(比如 Redis 集群的主从节点通过 RDB 文件同步数据)。

Redis 不同于 Memcached 的很重要一点就是,Redis 支持持久化,而且支持 3 种持久化方式:

  • 快照(snapshotting,RDB)
  • 只追加文件(append-only file, AOF)
  • RDB 和 AOF 的混合持久化(Redis 4.0 新增)

官方文档地址:https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/

本文基于 Redis 7.0+ 版本。不同版本的持久化机制有重要差异,使用前请确认你的 Redis 版本:

版本 持久化默认方式 重要特性
Redis 4.0 RDB 引入 RDB+AOF 混合持久化
Redis 6.0 RDB AOF 仍需手动开启
Redis 7.0 RDB 引入 Multi-Part AOF
Redis 7.2+ RDB 进一步优化持久化性能

关键行为差异

  • AOF rewrite 内存占用:Redis 7.0 之前重写期间增量数据需在内存中保留,7.0+ 使用 Multi-Part AOF 解决
  • 混合持久化:Redis 4.0-6.0 需手动开启,Redis 7.0 仍支持但需配置

检查你的 Redis 版本:

redis-cli INFO server | grep redis_version
# 输出示例:redis_version:7.0.12

RDB 持久化

什么是 RDB 持久化?

Redis 可以通过创建快照来获得存储在内存里面的数据在 某个时间点 上的副本。Redis 创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis 主从结构,主要用来提高 Redis 性能),还可以将快照留在原地以便重启服务器的时候使用。

快照持久化是 Redis 默认采用的持久化方式,在 redis.conf 配置文件中默认有此下配置:

# Redis 7.0 默认配置(单行格式)
save 3600 1 300 100 60 10000

# 各条件含义:
# - 3600 秒(1 小时)内至少有 1 个 key 变化
# - 300 秒(5 分钟)内至少有 100 个 key 变化
# - 60 秒(1 分钟)内至少有 10000 个 key 变化

# 等价于旧版多行格式:
# save 3600 1
# save 300 100
# save 60 10000

RDB 创建快照时会阻塞主线程吗?

Redis 提供了两个命令来生成 RDB 快照文件:

  • save : 同步保存操作,会阻塞 Redis 主线程;
  • bgsave : fork 出一个子进程,子进程执行。

这里说 Redis 主线程而不是主进程的主要是因为 Redis 启动之后主要是通过单线程的方式完成主要的工作。如果你想将其描述为 Redis 主进程,也没毛病。

fork 性能开销分析

虽然 bgsave 在子进程中执行,不会阻塞主线程处理命令请求,但 fork 操作本身是阻塞的,且会带来额外的内存开销:

数据集大小 fork 延迟 额外内存占用 风险等级
< 1GB < 10ms ~10MB (页表复制)
1-10GB 10-100ms 10-100MB
10-50GB 100ms-1s 100-500MB
> 50GB > 1s > 500MB 极高

Copy-on-Write (COW) 机制

  • fork 后,子进程共享父进程的内存页(标准页 4KB)
  • 当父进程或子进程修改内存页时,内核复制该页(Copy-on-Write)
  • 大数据集 + 高写负载时,会导致大量页面复制,影响性能

致命风险:THP(透明大页)导致的内存雪崩

Linux 发行版默认开启 THP(Transparent Huge Pages,透明大页),大小为 2MB。如果开启 THP,即使客户端仅修改了 10 字节的数据,内核也会强制复制完整的 2MB 内存页。这会导致 COW 的内存分配放大 512 倍(2MB / 4KB = 512)。

在高并发写入场景下,这会瞬间吸干宿主机内存,触发 OOM Killer 强杀 Redis 进程

验证方式

cat /sys/kernel/mm/transparent_hugepage/enabled
# 输出 [always] madvise never 表示已开启(危险!)
# 应该输出 always madvise [never]

解决方案:在 Redis 启动脚本中添加 echo never > /sys/kernel/mm/transparent_hugepage/enabled,或使用 redis-server --disable-thp yes(Redis 7.0+ 支持)。

启动警告:Redis 检测到 THP 开启时会在启动日志中打印 WARNING you have Transparent Huge Pages (THP) support enabled in your kernel,必须立即处理。

生产环境建议

# 1. 监控 fork 风险指标
redis-cli INFO memory | grep used_memory_rss  # RSS 内存
redis-cli INFO memory | grep used_memory       # 数据内存

# 计算 RSS/USED 比值,fork 时应 < 2
# 如果接近或超过 2,说明 fork 风险高

# 2. 设置 maxmemory 限制 Redis 内存占用,为 fork 预留空间
# 在 redis.conf 中设置:
# maxmemory 8gb
# maxmemory-policy allkeys-lru

# 3. 避免在高峰期手动触发 BGSAVE
# 让 Redis 根据配置规则自动触发

# 4. 考虑主从复制 + 从节点持久化架构
# 将持久化操作转移到从节点,避免主节点 fork 开销

监控告警

  • rdb_last_bgsave_time_sec:上次 bgsave 耗时,应 < 5s
  • rdb_last_cow_size:上次 fork 的 COW 内存大小,应 < 10% used_memory

AOF 持久化

什么是 AOF 持久化?

与快照持久化相比,AOF 持久化的实时性更好。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启:

版本说明:Redis 默认使用 RDB 持久化方式。若需使用 AOF,需要手动设置 appendonly yes。Redis 7.0 引入了 Multi-Part AOF 机制优化 AOF 性能,但并未改变默认持久化方式。

appendonly yes

开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入到 AOF 缓冲区 server.aof_buf 中,然后再写入到 AOF 文件中(此时还在系统内核缓存区未同步到磁盘),最后再根据持久化方式( fsync策略)的配置来决定何时将系统内核缓存区的数据同步到硬盘中的。

只有同步到磁盘中才算持久化保存了,否则依然存在数据丢失的风险,比如说:系统内核缓存区的数据还未同步,磁盘机器就宕机了,那这部分数据就算丢失了。

AOF 文件的保存位置和 RDB 文件的位置相同,都是通过 dir 参数设置的,默认的文件名是 appendonly.aof

AOF 工作基本流程是怎样的?

AOF 持久化功能的实现可以简单分为 5 步:

  1. 命令追加(append):所有的写命令会追加到 AOF 缓冲区中。
  2. 文件写入(write):将 AOF 缓冲区的数据写入到 AOF 文件中。这一步需要调用write函数(系统调用),write将数据写入到了系统内核缓冲区之后直接返回了(延迟写)。注意!!!此时并没有同步到磁盘。
  3. 文件同步(fsync):这一步才是持久化的核心!根据你在 redis.conf 文件里 appendfsync 配置的策略,Redis 会在不同的时机,调用 fsync 函数(系统调用)。fsync 针对单个文件操作,对其进行强制硬盘同步(文件在内核缓冲区里的数据写到硬盘),fsync 将阻塞直到写入磁盘完成后返回,保证了数据持久化。
  4. 文件重写(rewrite):随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
  5. 重启加载(load):当 Redis 重启时,可以加载 AOF 文件进行数据恢复。

Linux 系统直接提供了一些函数用于对文件和设备进行访问和控制,这些函数被称为 系统调用(syscall)

这里对上面提到的一些 Linux 系统调用再做一遍解释:

  • write:写入系统内核缓冲区之后直接返回(仅仅是写到缓冲区),不会立即同步到硬盘。虽然提高了效率,但也带来了数据丢失的风险。同步硬盘操作取决于 Linux 内核的脏页回写策略(Dirty Page Writeback),主要受以下参数影响:
    • /proc/sys/vm/dirty_expire_centisecs:脏页过期时间(默认 30 秒)
    • /proc/sys/vm/dirty_writeback_centisecs:内核回写线程的唤醒间隔(默认 5 秒)
    • 系统内存压力:内存不足时会更积极触发同步
  • 这意味着 appendfsync no 模式下宕机时,可能丢失的数据量是不可控且不可预测的,取决于上次内核同步的时间点。
  • fsyncfsync用于强制刷新系统内核缓冲区(同步到到磁盘),确保写磁盘操作结束才会返回。

AOF 工作流程图如下:

AOF 工作基本流程

AOF 持久化方式有哪些?

在 Redis 的配置文件中存在三种不同的 AOF 持久化方式( fsync策略),它们分别是:

  1. appendfsync always:主线程调用 write 执行写操作后,会立刻调用 fsync 函数同步 AOF 文件(刷盘)。主线程会阻塞,直到 fsync 将数据完全刷到磁盘后才会返回。这种方式数据最安全,理论上不会有任何数据丢失。但因为每个写操作都会同步阻塞主线程,所以性能极差。
  2. appendfsync everysec:主线程调用 write 执行写操作后立即返回,由后台线程( aof_fsync 线程)每秒钟调用 fsync 函数(系统调用)同步一次 AOF 文件(write+fsyncfsync间隔为 1 秒)。这种方式主线程的性能基本不受影响。在性能和数据安全之间做出了绝佳的平衡。不过,在 Redis 异常宕机时,通常可能丢失最近 1 秒内的数据。

生产级真相(2 秒丢失与阻塞风险)

"最多丢失 1 秒"是理想情况。当磁盘 I/O 繁忙时,后台 fsync 执行时间过长,主线程在执行写命令时会检查上一次 fsync 的完成时间。如果距离上次成功 fsync 超过 2 秒,主线程将被强制阻塞以保护内存不被撑爆(Redis 源码 aof.c 中的 aof_background_fsync 阻塞判断逻辑)。

因此,极端宕机情况下,可能会丢失最多 2 秒的数据,且磁盘抖动会直接导致 Redis P99 延迟飙升。

必须监控指标redis-cli INFO persistence | grep aof_delayed_fsync(记录主线程被 fsync 阻塞的累计次数)。3. appendfsync no:主线程调用 write 执行写操作后立即返回,让操作系统决定何时进行同步,Linux 下一般为 30 秒一次(write但不fsyncfsync 的时机由操作系统决定)。 这种方式性能最好,因为避免了 fsync 的阻塞。但数据安全性最差,宕机时丢失的数据量不可控,取决于操作系统上一次同步的时间点。

可以看出:这 3 种持久化方式的主要区别在于 fsync 同步 AOF 文件的时机(刷盘)

为了兼顾数据和写入性能,可以考虑 appendfsync everysec 选项 ,让 Redis 每秒同步一次 AOF 文件,Redis 性能受到的影响较小。通常情况下,即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis 还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。

⚠️ 注意:当磁盘 I/O 瓶颈严重时,Redis 主线程可能因等待 fsync 而阻塞长达 2 秒,期间数据丢失窗口扩大至 2 秒。生产环境应监控 aof_delayed_fsync 指标来评估磁盘健康度。

从 Redis 7.0.0 开始,Redis 使用了 Multi Part AOF 机制。顾名思义,Multi Part AOF 就是将原来的单个 AOF 文件拆分成多个 AOF 文件。在 Multi Part AOF 中,AOF 文件被分为三种类型,分别为:

  • BASE:表示基础 AOF 文件,它一般由子进程通过重写产生,该文件最多只有一个。
  • INCR:表示增量 AOF 文件,它一般会在 AOFRW 开始执行时被创建,该文件可能存在多个。
  • HISTORY:表示历史 AOF 文件,它由 BASE 和 INCR AOF 变化而来,每次 AOFRW 成功完成时,本次 AOFRW 之前对应的 BASE 和 INCR AOF 都将变为 HISTORY,HISTORY 类型的 AOF 会被 Redis 自动删除。

Multi Part AOF 不是重点,了解即可,详细介绍可以看看阿里开发者的Redis 7.0 Multi Part AOF 的设计和实现 这篇文章。

相关 issueRedis 的 AOF 方式 #783

AOF 为什么是在执行完命令之后记录日志?

关系型数据库(如 MySQL)通常都是执行命令之前记录日志(方便故障恢复),而 Redis AOF 持久化机制是在执行完命令之后再记录日志。

AOF 记录日志过程

为什么是在执行完命令之后记录日志呢?

  • 避免额外的检查开销,AOF 记录日志不会对命令进行语法检查;
  • 在命令执行完之后再记录,不会阻塞当前的命令执行。

这样也带来了风险(我在前面介绍 AOF 持久化的时候也提到过):

  • 如果刚执行完命令 Redis 就宕机会导致对应的修改丢失;
  • 可能会阻塞后续其他命令的执行(AOF 记录日志是在 Redis 主线程中进行的)。

AOF 重写了解吗?

当 AOF 变得太大时,Redis 能够在后台自动重写 AOF 产生一个新的 AOF 文件,这个新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样,但体积更小。

AOF 重写

AOF 重写(rewrite) 是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无须对现有 AOF 文件进行任何读入、分析或者写入操作。

由于 AOF 重写会进行大量的写入操作,为了避免对 Redis 正常处理命令请求造成影响,Redis 将 AOF 重写程序放到子进程里执行。

AOF 文件重写期间,Redis 还会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新的 AOF 文件保存的数据库状态与现有的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作。

开启 AOF 重写功能,可以调用 BGREWRITEAOF 命令手动执行,也可以设置下面两个配置项,让程序自动决定触发时机:

  • auto-aof-rewrite-min-size:如果 AOF 文件大小小于该值,则不会触发 AOF 重写。默认值为 64 MB;
  • auto-aof-rewrite-percentage:执行 AOF 重写时,当前 AOF 大小(aof_current_size)和上一次重写时 AOF 大小(aof_base_size)的比值。如果当前 AOF 文件大小增加了这个百分比值,将触发 AOF 重写。将此值设置为 0 将禁用自动 AOF 重写。默认值为 100。

AOF rewrite 的失败边界与风险场景

虽然 AOF rewrite 放在子进程执行,但仍存在以下风险需要了解:

风险场景 影响 触发条件 应对措施
fork 失败 无法创建 rewrite 子进程 内存不足、系统限制 监控内存使用率,设置 maxmemory
磁盘满 新 AOF 文件写入失败 rewrite 期间数据量增长快 监控磁盘使用率(df -h),设置告警阈值 70%
inode 耗尽 无法创建新文件 小文件过多的系统 监控 inode 使用率(df -i),清理临时文件
时间戳回拨 Multi-Part AOF 文件管理混乱 虚拟机时钟同步问题 配置 NTP 服务,设置 aof-timestamp-enabled
SIGTERM 信号 rewrite 被中断 运维人员手动重启 配置优雅关闭(shutdown-timeout

生产环境监控建议

# 监控 AOF rewrite 状态
redis-cli INFO persistence | grep aof_rewrite_in_progress

# 监控 AOF 文件大小增长
redis-cli INFO persistence | grep aof_current_size
redis-cli INFO persistence | grep aof_base_size

# 检查磁盘和 inode 使用率
df -h /var/lib/redis
df -i /var/lib/redis

# 设置 AOF rewrite 期间增量 fsync 策略(Redis 7.0+)
# aof-rewrite-incremental-sync yes

Redis 7.0 版本之前,如果在重写期间有写入命令,AOF 可能会使用大量内存,重写期间到达的所有写入命令都会写入磁盘两次。

Redis 7.0 版本之后,AOF 重写机制得到了优化改进。下面这段内容摘自阿里开发者的从 Redis7.0 发布看 Redis 的过去与未来 这篇文章。

AOF 重写期间的增量数据如何处理一直是个问题,在过去写期间的增量数据需要在内存中保留,写结束后再把这部分增量数据写入新的 AOF 文件中以保证数据完整性。可以看出来 AOF 写会额外消耗内存和磁盘 IO,这也是 Redis AOF 写的痛点,虽然之前也进行过多次改进但是资源消耗的本质问题一直没有解决。

阿里云的 Redis 企业版在最初也遇到了这个问题,在内部经过多次迭代开发,实现了 Multi-part AOF 机制来解决,同时也贡献给了社区并随此次 7.0 发布。具体方法是采用 base(全量数据)+inc(增量数据)独立文件存储的方式,彻底解决内存和 IO 资源的浪费,同时也支持对历史 AOF 文件的保存管理,结合 AOF 文件中的时间信息还可以实现 PITR 按时间点恢复(阿里云企业版 Tair 已支持),这进一步增强了 Redis 的数据可靠性,满足用户数据回档等需求。

相关 issueRedis AOF 重写描述不准确 #1439

AOF 校验机制了解吗?

纯 AOF 模式下,Redis 不会对整个 AOF 文件使用校验和(如 CRC64),而是通过逐条解析文件中的命令来验证文件的有效性。如果解析过程中发现语法错误(如命令不完整、格式错误),Redis 会终止加载并报错,从而避免错误数据载入内存。

尾部截断容灾(自动恢复)

在遭遇意外断电或 kill -9 强制终止时,AOF 文件的最后一条命令极可能写入不完整(只写了一半)。此时的恢复行为由 aof-load-truncated 配置决定:

配置值 行为 适用场景
yes(默认) Redis 自动丢弃文件尾部不完整的命令,继续完成启动并在日志中打印警告信息 生产环境推荐,允许少量数据丢失换取可用性
no Redis 拒绝启动并直接报错,强制要求人工使用 redis-check-aof 工具确认并修复数据 金融等对数据完整性要求极高的场景

验证截断恢复

# 模拟断电场景:向 AOF 文件追加无意义的乱码
echo "truncated garbage data" >> /var/lib/redis/appendonly.aof

# 重启 Redis(aof-load-truncated=yes 时会自动恢复)
redis-server /path/to/redis.conf
# 日志输出:# Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>

失败模式:如果 AOF 文件的中间部分(而非尾部)因为磁盘静默损坏出现乱码,自动截断机制无效,Redis 将直接宕机拒绝服务。此时需要使用 redis-check-aof --fix 工具修复。

混合持久化模式(Redis 4.0 引入)下,AOF 文件由两部分组成:

  • RDB 快照部分:文件以固定的 REDIS 字符开头,存储某一时刻的内存数据快照,并在快照数据末尾附带一个 CRC64 校验和(位于 RDB 数据块尾部、AOF 增量部分之前)。
  • AOF 增量部分:紧随 RDB 快照部分之后,记录 RDB 快照生成后的增量写命令。这部分增量命令以 Redis 协议格式逐条记录,无整体或全局校验和。

RDB 文件结构的核心部分如下:

字段 解释
"REDIS" 固定以该字符串开始
RDB_VERSION RDB 文件的版本号
DB_NUM Redis 数据库编号,指明数据需要存放到哪个数据库
KEY_VALUE_PAIRS Redis 中具体键值对的存储
EOF RDB 文件结束标志
CHECK_SUM 8 字节确保 RDB 完整性的校验和

Redis 启动并加载 AOF 文件时,首先会校验文件开头 RDB 快照部分的数据完整性,即计算该部分数据的 CRC64 校验和,并与紧随 RDB 数据之后、AOF 增量部分之前存储的 CRC64 校验和值进行比较。如果 CRC64 校验和不匹配,Redis 将拒绝启动并报告错误。

RDB 部分校验通过后,Redis 随后逐条解析 AOF 部分的增量命令。如果解析过程中出现错误(如不完整的命令或格式错误),Redis 会停止继续加载后续命令,并报告错误,但此时 Redis 已经成功加载了 RDB 快照部分的数据。

新版本优化

Redis 4.0 对于持久化机制做了什么优化?

由于 RDB 和 AOF 各有优势,于是,Redis 4.0 开始支持 RDB 和 AOF 的混合持久化。

配置说明

# 开启 AOF
appendonly yes

# 开启混合持久化(Redis 7.0+ 默认启用)
aof-use-rdb-preamble yes

# 优化重写触发条件
auto-aof-rewrite-percentage 100   # AOF 文件大小比上次重写后增长 100% 时触发
auto-aof-rewrite-min-size 64mb    # AOF 文件至少达到 64MB 才触发重写

版本差异

  • Redis 4.0-6.x:混合持久化默认关闭,需手动配置 aof-use-rdb-preamble yes
  • Redis 7.0+:混合持久化默认启用,无需额外配置

工作原理

如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。

混合持久化文件结构

┌───────────────────┐
│   RDB Header      │ ← 二进制快照(压缩格式)
│   REDIS0009       │
│   ...             │
├───────────────────┤
│   AOF Log Entries │ ← 文本格式命令
│   *3\r\n$3\r\nSET\r\n$5\r\nkey01\r\n...
│   INCR counter    │
│   ...             │
└───────────────────┘

核心工作流程

  1. 写处理阶段

    • 客户端执行写命令(SET/INCR 等)
    • Redis 立即更新内存数据
    • 将命令追加到 AOF 缓冲区(文本格式)
  2. 持久化触发阶段

    • AOF 文件大小达到阈值(默认 64MB)或增长 100%
    • 触发 AOF 重写(BGREWRITEAOF
  3. 文件构建阶段

    • 子进程将当前内存数据以 RDB 格式写入新 AOF 文件开头
    • 父进程继续处理写命令,增量数据记录到重写缓冲区
    • 重写完成后,将重写缓冲区的增量命令追加到新 AOF 文件末尾
  4. 数据恢复阶段

    • Redis 启动时优先加载 RDB 部分(快速恢复基础数据)
    • 然后顺序重放 AOF 增量命令(恢复最新数据)

优势对比

指标 纯 RDB 纯 AOF 混合持久化
恢复速度 快(秒级) 慢(分钟级) 快(秒级)
数据丢失窗口 分钟级 ≤2 秒 ≤2 秒
文件大小 小(压缩) 大(文本日志) 中等
写入影响 中等
可读性 差(二进制) 好(文本) 差(RDB 部分)

基准数据(1GB 数据集,SSD):

  • 纯 AOF 恢复:30-60 秒
  • 混合持久化恢复:2-5 秒(快 5-10 倍

生产配置建议

# 完整生产配置示例
appendonly yes
aof-use-rdb-preamble yes

# 性能优化
aof-rewrite-incremental-fsync yes   # 增量 fsync,减少磁盘 I/O 峰值
no-appendfsync-on-rewrite no        # 重写期间仍执行 fsync(推荐)

# 容量规划建议:
# - 预留 2x 内存作为磁盘空间
# - 保持单个 AOF 文件 < 16GB
# - 监控 aof_delayed_fsync 指标

常见问题及解决方案

1. 配置验证

# 方法 1:检查文件头(输出 REDIS 表示启用了混合持久化)
head -c 5 appendonly.aof

# 方法 2:CLI 验证
redis-cli CONFIG GET aof-use-rdb-preamble
# 输出:1) "aof-use-rdb-preamble"
#      2) "yes"

2. 文件损坏恢复

# 修复 RDB 部分
redis-check-rdb --fix appendonly.aof

# 修复 AOF 部分
redis-check-aof --fix appendonly.aof

# 启动 Redis
redis-server --appendonly yes --appendfilename appendonly.aof

缺点

  • AOF 文件里面的 RDB 部分是压缩格式,不再是 AOF 格式,可读性较差。
  • 需要额外消耗 CPU 进行 RDB 压缩和解压。

官方文档地址:https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/

Redis 7.0 对于持久化机制做了什么优化?

由于 AOF 重写过程中存在内存缓冲增量数据和磁盘双写的问题,于是,Redis 7.0 开始支持 Multi-Part AOF(默认启用,可以通过配置项 appenddirname 指定目录)。

如果把 Multi-Part AOF 启用,AOF 文件将被拆分为 base 文件(最多一个,初始全量快照,可为 RDB 或 AOF 格式)和多个 incr 文件(增量命令日志),重写期间新增命令直接写入新的 incr 文件,由 manifest 文件跟踪所有部分。这样做的好处是可以消除重写时的内存缓冲开销和双重 I/O 写入,提高性能并减少潜在的 fsync 冻结。由于文件结构分离,INCR 文件在重写前保持只读,单文件拷贝相对安全;但跨文件的一致性备份仍需暂停重写,整体备份流程比单文件 AOF 更复杂,且在极大数据集下仍可能需监控资源。

核心单点故障风险:manifest 文件损坏

Multi-Part AOF 依赖 manifest 文件来跟踪和管理所有 base/incr/history 文件,这是整个增量日志体系的核心元数据。如果 manifest 文件损坏或丢失:

风险场景 影响 恢复难度
manifest 静默损坏 Redis 启动时无法正确识别和加载 AOF 文件,数据库无法恢复 极高(需手动重建 manifest)
磁盘故障导致 manifest 丢失 即使 base/incr 文件完整,Redis 也无法重构文件依赖关系 极高(需人工干预)

缓解措施

# 1. 备份 manifest 文件(与数据文件同等重要)
cp /var/lib/redis/appendonlydir/appendonly.aof.manifest /backup/

# 2. 监控磁盘健康度(提前发现故障)
smartctl -a /dev/sda | grep -E "SMART overall-health self-assessment|Media_Errors"

# 3. 定期验证 manifest 完整性(Redis 启动时会自动校验)
redis-check-aof /var/lib/redis/appendonlydir/appendonly.aof.manifest

官方未提供自动化修复工具,生产环境必须将 manifest 文件纳入备份策略,其重要性等同于 RDB/AOF 数据文件本身。

生产环境监控指标

持久化性能指标

# RDB 相关指标
redis-cli INFO persistence | grep rdb_last_bgsave_time_sec
# 建议:< 5s。超过 5s 说明数据集过大或 I/O 性能瓶颈

redis-cli INFO persistence | grep rdb_last_cow_size
# 建议:< 10% used_memory。超过说明 fork 的 Copy-on-Write 内存开销大

redis-cli INFO memory | grep used_memory_rss
redis-cli INFO memory | grep used_memory
# 计算:used_memory_rss / used_memory,fork 时应 < 2

# AOF 相关指标
redis-cli INFO persistence | grep aof_rewrite_in_progress
# 期望:0(未在重写)或 1(正在重写)

redis-cli INFO persistence | grep aof_current_size
redis-cli INFO persistence | grep aof_base_size
# 监控增长率,避免 rewrite 过于频繁

redis-cli INFO persistence | grep aof_buffer_length
# 建议:< 4MB。过大说明主线程写入速度快于 fsync 速度

系统资源监控

# 磁盘使用率和 I/O 等待
iostat -x 1 5 | grep dm-0
# 关注:%util(I/O 使用率)、await(平均等待时间)

# 磁盘空间(预留空间给 rewrite 生成新文件)
df -h /var/lib/redis
# 建议:使用率 < 70%

# inode 使用率(小文件多的场景)
df -i /var/lib/redis
# 建议:使用率 < 90%

# 内存使用率
free -h
# 建议:为 fork 预留至少 20% 空闲内存

告警规则建议

alert_rules:
  - name: "Redis fork 风险高"
    expr: redis_rss_memory / redis_used_memory > 2
    for: 5m
    annotations:
      summary: "Redis fork 风险过高,可能导致 OOM"
      description: "RSS/USED 比值超过 2,fork 时会复制大量页表"

  - name: "AOF rewrite 过于频繁"
    expr: rate(aof_current_size[5m]) > 10485760 # 增长 > 10MB/min
    for: 5m
    annotations:
      summary: "AOF rewrite 触发过于频繁"
      description: "增量数据增长过快,可能存在 write 放大问题"

  - name: "磁盘使用率过高"
    expr: disk_usage > 70
    for: 5m
    annotations:
      summary: "Redis 磁盘空间不足"
      description: "磁盘使用率超过 70%,可能无法完成 AOF rewrite"

  - name: "AOF fsync 延迟导致主线程阻塞"
    expr: rate(redis_aof_delayed_fsync[5m]) > 0
    for: 2m
    annotations:
      summary: "Redis AOF fsync 延迟过高,影响业务 P99 延迟"
      description: "主线程因等待 fsync 而被阻塞(aof_delayed_fsync > 0),磁盘 I/O 瓶颈或 fsync 频率过高,可能影响业务响应时间"

如何选择 RDB 和 AOF?

关于 RDB 和 AOF 的优缺点,官网上面也给了比较详细的说明Redis persistence,这里结合自己的理解简单总结一下。

RDB 比 AOF 优秀的地方

  • RDB 文件存储的内容是经过压缩的二进制数据, 保存着某个时间点的数据集,文件很小,适合做数据的备份,灾难恢复。AOF 文件存储的是每一次写命令,类似于 MySQL 的 binlog 日志,通常会比 RDB 文件大很多。当 AOF 变得太大时,Redis 能够在后台自动重写 AOF。新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样,但体积更小。不过, Redis 7.0 版本之前,如果在重写期间有写入命令,AOF 可能会使用大量内存,重写期间到达的所有写入命令都会写入磁盘两次。
  • 使用 RDB 文件恢复数据,直接解析还原数据即可,不需要一条一条地执行命令,速度非常快。而 AOF 则需要依次执行每个写命令,速度非常慢。也就是说,与 AOF 相比,恢复大数据集的时候,RDB 速度更快。

AOF 比 RDB 优秀的地方

  • RDB 的数据安全性不如 AOF,没有办法实时或者秒级持久化数据。生成 RDB 文件的过程是比较繁重的, 虽然 BGSAVE 子进程写入 RDB 文件的工作不会阻塞主线程,但会对机器的 CPU 资源和内存资源产生影响,严重的情况下甚至会直接把 Redis 服务干宕机。AOF 支持秒级数据丢失(取决于 fsync 策略,如果是 everysec,通常最多丢失 1 秒的数据;但磁盘 I/O 繁忙时可能丢失 2 秒且主线程会阻塞),仅仅是追加命令到 AOF 文件,操作轻量。
  • RDB 文件是以特定的二进制格式保存的,并且在 Redis 版本演进中有多个版本的 RDB,所以存在老版本的 Redis 服务不兼容新版本的 RDB 格式的问题。
  • AOF 以一种易于理解和解析的格式包含所有操作的日志。你可以轻松地导出 AOF 文件进行分析,你也可以直接操作 AOF 文件来解决一些问题。比如,如果执行FLUSHALL命令意外地刷新了所有内容后,只要 AOF 文件没有被重写,删除最新命令并重启即可恢复之前的状态。

综上

  • Redis 保存的数据丢失一些也没什么影响的话,可以选择使用 RDB。
  • 不建议单独使用 AOF,因为时不时地创建一个 RDB 快照可以进行数据库备份、更快的重启以及解决 AOF 引擎错误。
  • 如果保存的数据要求安全性比较高的话,建议同时开启 RDB 和 AOF 持久化或者开启 RDB 和 AOF 混合持久化。

参考