登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  Redis

Redis 多段 AOF 文件怎么组织与重写

来源:17golang原创

时间:2026-10-04 11:49:19 299浏览 收藏

Redis 7.0 起,AOF 不再只由一个不断增长的文件承担全部数据,而是组织成一套多段文件:最多一个 BASE 文件、一个或多个 INCR 文件,以及负责描述有效文件集合的 manifest。BASE 表示最近一次重写时的数据基线,INCR 保存此后持续到来的增量写命令;Redis 启动时按照 manifest 指向的集合加载,而不是看到目录里有什么就把什么都重放。

BGREWRITEAOF 的关键也不再是“用一个新文件覆盖旧文件”。重写开始后,父进程会打开新的 INCR 文件继续接收写入,子进程生成新的 BASE;两者准备好后组成临时 manifest,最后通过 manifest 的原子替换让新一代集合生效,再清理不再引用的旧文件。官方持久化说明:https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/

多段 AOF 解决了什么问题

AOF 记录 Redis 接收到的写操作,重启时重放这些命令来恢复数据。问题是日志会随着写入持续增长:同一个计数器增加 100 次,最终数据集可能只需要一个键和一个结果值,但旧 AOF 中保留了 100 条增量命令。重写的目标就是用能重建当前内存数据集的更紧凑表示替代冗余历史。

Redis 7.0 以前,重写期间到来的写命令既要继续写旧 AOF,又会进入内存缓冲区;重写子进程完成后,父进程再把这段缓冲追加到新文件尾部。写入密集时,这种方式会带来额外内存、双写和重写尾部合并压力。

Redis 7.0 的多段 AOF 将“基线”和“重写期间及之后的新写入”拆成不同文件。父进程可以把新写命令直接追加到新的 INCR 文件,子进程独立构造新 BASE,最后只需要用 manifest 切换哪组文件有效。它没有取消 fork、写盘和写时复制成本,但把文件组织和切换边界变得更清楚。

维度Redis 7.0 以前Redis 7.0 及以上
AOF 组织主要是单个 AOF 文件BASE + 多个 INCR + manifest
重写期间新写入旧 AOF 与内存重写缓冲共同承接父进程打开新的 INCR 文件持续追加
切换对象新旧 AOF 文件manifest 引用的整套文件集合
失败保护保留旧 AOF旧集合加当前 INCR 仍能表示完整数据

这里的“多段”不是把一个文件随意切成若干固定大小分片,也不是让运维人员按日期手工滚动。文件代际与清单都由 Redis 管理,目录中的文件名和 manifest 内容应视为持久化内部状态。

BASE、INCR 和 manifest 怎样组成可加载集合

多段 AOF 的文件放在 appenddirname 指定的独立目录中。理解目录时,先区分三个角色:

  • BASE:最多一个,表示某次重写时内存数据集的基线。它可以采用 RDB 格式,也可以采用 AOF 格式。
  • INCR:可以有多个,记录对应 BASE 创建后发生的增量写入。
  • manifest:列出当前有效的 BASE 与 INCR 文件,是 Redis 判断加载集合的依据。
appenddirname 目录中 manifest、BASE、多个 INCR 与启动加载集合的关系
图1:多段 AOF 文件集合的静态组成关系。它是结构图,不是服务器目录截图。

启动恢复的逻辑可以理解为:先读取 manifest,再加载 manifest 引用的 BASE,最后按清单顺序应用对应的 INCR。目录里可能短时间存在旧代文件、临时文件或等待清理的文件,仅凭文件名排序拼接并不可靠。

因此,下面几种手工操作都不安全:

  • 只复制看起来最大的 BASE 文件,遗漏其后的 INCR。
  • 按修改时间挑选 INCR,而不保留与之匹配的 manifest。
  • 重写过程中删除“旧编号”文件,破坏当前仍有效的集合。
  • 自行编辑 manifest,把未验证的文件拼成一套恢复链。

需要备份时,目标不是某一个 AOF 文件,而是一个一致的 manifest 及其引用文件集合。

最小配置与状态检查

启用 AOF 的核心配置仍然是 appendonly yes。多段组织是 Redis 7.0+ 的内部机制,不需要额外开启“分段开关”。可以在配置文件中明确目录名和刷盘策略:

# 启用 AOF 持久化
appendonly yes

# 多段 AOF 文件集合使用的子目录名
appenddirname "appendonlydir"

# 默认每秒刷盘,性能与可能丢失约一秒写入之间取平衡
appendfsync everysec

# 允许重写产生 RDB 格式的 BASE,以缩短加载并减小体积
aof-use-rdb-preamble yes

appenddirname 是相对于 Redis 工作目录 dir 的子目录配置。不要把它和 appendfilename 简单理解为同一级的单文件路径;在多段 AOF 下,真正要管理的是目录中的整套文件。

运行中的实例可以用 INFO persistence 检查是否启用、是否正在重写、是否有待调度任务以及最近一次结果:

# 只读取持久化区段,避免在巡检时输出无关信息
redis-cli INFO persistence

# 重点筛选 AOF 启用、重写进度和最近一次状态
redis-cli INFO persistence | grep -E '^(aof_enabled|aof_rewrite_in_progress|aof_rewrite_scheduled|aof_last_bgrewrite_status|aof_current_size|aof_base_size):'

常用判断可以归纳为:

字段含义运维判断
aof_enabledAOF 是否启用应与配置和数据安全策略一致
aof_rewrite_in_progress是否正在重写为 1 时不要做依赖稳定文件集合的备份
aof_rewrite_scheduled是否等待其他持久化子进程结束后执行命令返回成功不等于已经开始
aof_last_bgrewrite_status最近一次后台重写状态应持续监控是否为 ok
aof_current_size当前 AOF 总体大小与基线大小结合判断增长幅度
aof_base_size最近启动或重写时的基线大小用于理解自动重写阈值的参照

如果同时存在 RDB 后台保存,BGREWRITEAOF 可能只被调度,等保存子进程结束后才真正开始。这时要同时观察 rdb_bgsave_in_progress、aof_rewrite_scheduled 和 aof_rewrite_in_progress,不能只看命令返回的一行状态。

BGREWRITEAOF 如何生成新一代文件集合

BGREWRITEAOF 用于请求后台生成更紧凑的 AOF。命令本身的时间复杂度标记为 O(1),但这只描述命令触发动作,不代表后台重写没有成本。实际重写需要遍历当前数据集、生成新 BASE、维护增量写入并落盘。

# 请求后台重写;成功回复可能表示已开始,也可能表示已调度
redis-cli BGREWRITEAOF

# 持续查看真实状态,直到进行中与待调度字段都归零
redis-cli INFO persistence

Redis 7.0+ 的文件关系可以按下面的边界理解:

  1. 现行 manifest 继续引用旧 BASE 和旧 INCR,这套集合在重写完成前仍然有效。
  2. 父进程打开一个新的 INCR,用它持续接收重写期间到来的写命令。
  3. 子进程基于 fork 时刻的数据视图生成新的 BASE 临时文件。
  4. 新 BASE 与新 INCR 准备好后,Redis 构造并持久化临时 manifest。
  5. Redis 原子替换 manifest,使新一代文件集合生效,然后清理旧 BASE 和未再引用的 INCR。
现行 AOF 文件集合与重写候选集合通过 manifest 原子切换点建立关系
图2:AOF 重写前后的两代文件集合与清单切换边界。它是静态说明图,不是运行时序图。

这个设计的重要安全点是:切换发生在 manifest 层。新 BASE 尚未完成时,现行 manifest 仍指向旧集合;重写期间的新写入已经进入新 INCR。即使重写失败,旧 BASE、旧 INCR 加上这份新打开的 INCR 仍能表示更新后的完整数据,Redis 不需要让一个半成品 BASE 取代稳定集合。

官方命令说明也明确指出:如果 BGREWRITEAOF 失败,旧 AOF 不会因此被破坏。若已有 AOF 重写正在进行,再次调用会返回错误;若正在执行 RDB 保存,重写会被安排在该子进程结束后开始。

自动重写怎样触发

Redis 可以根据当前 AOF 大小相对基线的增长比例自动触发重写。常见配置由一个百分比和一个最小体积共同控制:

# 当前 AOF 相对基线增长到 100% 时具备自动重写条件
auto-aof-rewrite-percentage 100

# 文件至少达到 64mb 才触发,避免小数据集频繁重写
auto-aof-rewrite-min-size 64mb

只有百分比而没有最小体积,小实例也可能因相对增长很快而频繁重写;只有最小体积而没有增长比例,又可能让冗余日志长期累积。阈值应结合写入速率、可用内存、磁盘吞吐、恢复时间目标和重写耗时设置。

Redis 还会限制连续失败后的重试节奏,避免失败重写不断创建更多 INCR 文件并反复消耗资源。运维上不应只等待自动重试,而要检查最近一次重写状态、系统日志、磁盘空间、权限和 fork 失败原因。

兼容与迁移时要注意什么

Redis 7.0 前后的目录模型不同

Redis 7.0 以前通常围绕单个 AOF 文件做备份、监控和清理;升级后需要把工具改成“目录 + manifest 引用集合”的认知。仍然只复制 appendonly.aof 的旧脚本,可能无法获得完整备份。

BASE 可能是 RDB 格式

当 aof-use-rdb-preamble yes 时,BASE 可以用紧凑的 RDB 格式保存,后续 INCR 仍是 AOF 增量命令。这不代表实例退回了纯 RDB 持久化;恢复时仍由 manifest 组织 BASE 与 INCR 的完整集合。

不要把版本降级当作文件复制

多段 AOF 是 Redis 7.0+ 的持久化格式边界。需要降级到不理解该格式的旧版本时,应按照目标版本兼容文档设计数据迁移和回滚,不应假设把目录改名或把多个文件拼成一个文件就能安全启动。

从 RDB 切换到 AOF 要在运行实例上完成

官方建议在当前运行的 Redis 上通过配置命令启用 AOF,让 Redis 基于内存数据生成初始持久化集合,并把配置持久化到配置文件。只改配置文件后直接重启,可能让新实例找不到包含最新数据的 AOF。

# 在运行实例上启用 AOF,让 Redis 生成初始文件集合
redis-cli CONFIG SET appendonly yes

# 把有效配置写回配置文件,避免重启后回到旧设置
redis-cli CONFIG REWRITE

# 等待重写结束,并确认最近一次状态正常
redis-cli INFO persistence

执行前先备份最新 RDB,并根据权限与变更流程安排窗口。启用后还要确认写命令确实进入 AOF、重写状态结束,以及重启前后的关键数据量与业务校验一致。

备份多段 AOF 不能边重写边随意复制

正常运行时,完整复制或打包 appenddirname 目录即可取得多段 AOF 文件集合;但如果复制过程与 AOF 重写重叠,可能把旧 manifest、新 BASE 和不同代 INCR 混在一起,得到不可恢复的组合。

官方给出的基本思路是:临时关闭自动重写,确认没有重写正在进行,复制整个目录,完成后再恢复原阈值。操作时要记录原配置值,而不是一律恢复为示例数字:

# 临时关闭自动重写;先记录原值,完成后必须恢复
redis-cli CONFIG SET auto-aof-rewrite-percentage 0

# 确认 aof_rewrite_in_progress 为 0 后再复制整个 appenddirname
redis-cli INFO persistence

# 复制完成后恢复变更前的百分比,不要盲目使用固定值
redis-cli CONFIG SET auto-aof-rewrite-percentage 

这里还要避免手工调用 BGREWRITEAOF。如果希望缩短暂停自动重写的时间,可以先为目录中的不可变文件创建硬链接,再恢复重写设置,随后从硬链接集合复制;是否采用该方案取决于文件系统和备份工具能力。

性能与安全边界

多段 AOF 降低了 Redis 7.0 以前重写尾部缓冲的一部分压力,但重写依然不是“零成本”:

  • fork 延迟:数据集越大、页表越多,创建子进程可能越慢。
  • 写时复制:重写期间持续写入会让被修改内存页复制,额外内存应通过 current_cow_peak、aof_last_cow_size 等指标观察。
  • 磁盘带宽:子进程写新 BASE,父进程写 INCR,若与备份或 RDB 保存争抢磁盘,延迟可能上升。
  • fsync 策略:always、everysec 和 no 的性能与数据丢失窗口不同;多段文件不会改变这一基本取舍。
  • 磁盘空间:重写完成前,新旧两代文件可能同时存在,需要预留高于单套 AOF 的空间。

生产巡检可以同时关注持久化和系统资源,而不是只盯一个“重写成功”字段:

# 查看 AOF 当前大小、基线大小、重写状态与 COW 指标
redis-cli INFO persistence

# 查看延迟事件,结合系统监控定位 fork 或磁盘抖动
redis-cli LATENCY LATEST

# 查看当前工作目录与 AOF 子目录配置,确认容量监控覆盖正确路径
redis-cli CONFIG GET dir
redis-cli CONFIG GET appenddirname

Redis 管理命令通常属于敏感运维权限。生产环境应通过 ACL 限制 BGREWRITEAOF、CONFIG、INFO 等命令的调用范围,不要为了方便巡检而向普通应用账号开放完整管理权限。

一份可执行的检查清单

  1. 确认实例版本为 Redis 7.0 或更高,监控脚本已经适配多段 AOF。
  2. 确认 appendonly、appenddirname、appendfsync 与恢复目标一致。
  3. 确认容量监控覆盖整个 AOF 子目录,而不是只匹配单个旧文件名。
  4. 观察 aof_rewrite_in_progress、aof_rewrite_scheduled 和 aof_last_bgrewrite_status。
  5. 记录重写耗时、COW 峰值、磁盘吞吐、延迟和重写前后总体积。
  6. 备份时暂停自动与手工重写,并复制完整目录及 manifest 引用集合。
  7. 定期在隔离环境做恢复演练,不能把“文件复制成功”等同于“可恢复”。
  8. 重写失败时保留现场与旧集合,先查磁盘、权限、fork 和系统日志,不手工删文件。

常见问题

为什么目录里会有多个 INCR?

每次重写开始时,父进程都可能打开新的 INCR 来接收后续写入。重写失败或重试时,为保证数据链完整,旧集合与新开的 INCR 可能同时保留;Redis 会限制连续失败后的重试节奏,并在成功切换后清理不再使用的文件。

可以只备份 BASE 吗?

不可以把 BASE 当成最新完整状态。BASE 只代表最近一次基线,之后的写入在 INCR 中。恢复需要 manifest 指定的完整 BASE 与 INCR 集合。

BGREWRITEAOF 返回成功就代表完成了吗?

不代表。成功回复只说明重写已启动或已被调度。应使用 INFO persistence 等待 aof_rewrite_in_progress 和 aof_rewrite_scheduled 都为 0,并检查 aof_last_bgrewrite_status。

多段 AOF 会消除重写期间的内存压力吗?

不会。它减少了 Redis 7.0 以前重写缓冲与双写造成的一部分问题,但 fork 和写时复制仍会消耗内存。写入越密集,重写期间被修改的内存页越多,COW 峰值越值得关注。

manifest 能手工修复吗?

不建议把手工编辑 manifest 作为常规修复方案。先保留完整副本,查看 Redis 日志和官方修复工具适用范围,并在隔离环境验证恢复。清单、BASE 与 INCR 必须属于一致文件代,误配可能造成启动失败或数据缺失。

把多段 AOF 理解成“manifest 管理的一组不可随意拆分的持久化对象”,就能抓住组织与重写的核心:BASE 提供基线,INCR 接续变化,manifest 决定哪一代集合有效,原子清单切换保证半成品不会替换稳定数据。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>