AOF 重写流程与多部分 AOF

举报
yd_227606267 发表于 2026/11/08 08:11:02 2026/11/08
【摘要】 拆解 Redis 7 重写全流程与 aof-use-rdb-preamble 的作用

AOF 以追加方式记录写命令,时间一长文件会远大于数据集本身,恢复也随之变慢。重写是 AOF 体系的自我瘦身机制,Redis 7 又把它改造成多部分结构。本文拆解重写的触发条件与完整流程,说明 aof-use-rdb-preamble 在其中的角色。

一、AOF 为什么必须重写

对同一个 key 执行 100 次 INCR,AOF 里就有 100 条命令;某个 key 被删除后,它历史上所有写命令仍然躺在文件里。恢复时逐条重放这些冗余命令,既慢又占磁盘。

BGREWRITEAOF 解决这个问题:根据当前内存中的数据状态,生成一份"最小命令集"——每个 key 只保留恢复它所需的最少命令,已删除的 key 彻底消失。重写完成后,文件体积通常只有原来的一小部分。

127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started

二、自动触发的两个条件

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
  • auto-aof-rewrite-percentage 100:当前 AOF 体积比上次重写后增大 100% 时触发;
  • auto-aof-rewrite-min-size 64mb:文件小于 64mb 时即使翻倍也不重写,避免小实例反复 fork。

两个条件同时满足才会自动重写。把 percentage 设为 0 可完全关闭自动重写,只靠手动或定时任务触发。另外,如果 BGSAVE 正在执行,重写会被推迟(INFO persistence 里 aof_rewrite_scheduled 置 1),避免两个子进程同时抢 IO 与内存。

Redis 在 serverCron 里(默认每秒 10 次)检查重写条件,判断所需的数据来自 INFO persistence 的这两个字段:

redis-cli INFO persistence | grep -E "aof_base_size|aof_current_size"
# aof_base_size:67108864
# aof_current_size:184549376

aof_base_size 是上次重写完成时记录的体积,aof_current_size 是当前体积,增长率按 (aof_current_size * 100 / aof_base_size) - 100 计算。本例为 175%,超过 auto-aof-rewrite-percentage 100 的阈值,于是触发自动重写。

要注意比较对象是"上次重写后的体积"而不是"上一次检查时的体积",所以文件增长到恰好两倍时比例才是 100。这解释了一个常见现象:写入量平稳的实例,重写会呈现"按倍数阶梯"发生的节奏,而不是均匀分布。若实例长期没有重写(例如百分比设得很大),aof_base_size 会停留在很早的数值上,此时一次自动重写释放的空间可能远超预期。

三、Redis 7 的重写流程

Redis 7 之前,重写需要父进程维护一块重写缓冲区,把期间的新命令先攒起来,子进程结束后再追加到新文件,流程复杂且内存开销大。7.0 改用多部分 AOF 后,这套机制被整体移除:

  1. 父进程 fork 出子进程,子进程遍历当前内存,把数据集写成一份 base 临时文件(aof-use-rdb-preamble yes 时是 RDB 格式);
  2. 父进程同时打开一个新的 incr 文件,此后的写命令直接追加到这个新文件,不再需要缓冲区中转;
  3. 子进程写完 base 后通知父进程,父进程原子更新 manifest,把"新 base + 新 incr"登记为当前生效的文件组合;
  4. 旧的 incr 文件从 manifest 移除后异步删除。

整个过程中除 fork 瞬间外,命令处理不受阻塞;重写期间唯一需要关注的是磁盘——旧文件与新 base 短暂并存,容量要按约两倍 AOF 预留。

四、aof-use-rdb-preamble:base 文件的格式开关

aof-use-rdb-preamble yes

该参数决定 base 文件的编码:yes 时用 RDB 二进制格式,加载走 RDB 解析路径,速度比重放命令快一个量级;no 时 base 也是 RESP 命令文本。两种形态在数据目录里可以直接区分:

ls /var/lib/redis/appendonlydir/
# appendonly.aof.1.base.rdb      ← 混合格式(preamble yes)
# appendonly.aof.1.incr.aof
# appendonly.aof.manifest

aof-use-rdb-preamble no 时 base 文件的后缀是 .base.aof,内容可以直接用文本编辑器打开查看。该参数自 Redis 5.0 起默认为 yes,4.0 引入混合持久化时默认 no。

manifest 本身是一份普通文本,记录了当前生效的文件清单,可以直接查看:

cat /var/lib/redis/appendonlydir/appendonly.aof.manifest
# file appendonly.aof.1.base.rdb seq 1 type b
# file appendonly.aof.1.incr.aof seq 1 type i

三列分别是文件名、序号 seq 与类型 type:b 表示 base,i 表示 incr。恢复时 Redis 先读 base 再按 seq 顺序重放所有 incr。这也是重写能安全切换的原因——子进程只需把新 base 用递增的 seq 写入,再由父进程改写 manifest 这一行文本,切换动作足够小,不会出现"写了一半的 manifest"。

五、一次重写期间发生了什么

把几个观测点串起来,可以把重写理解成下面这条时间线。假设实例有 20gb 数据、AOF 已增长到 25gb:

  1. T0:serverCron 发现 aof_current_size / aof_base_size 超过阈值,调用 rewriteAppendOnlyFileBackground;
  2. T0+毫秒级:父进程 fork,页表复制产生短暂停顿,子进程开始遍历内存;
  3. T0~T1:父进程继续处理请求,写命令全部追加到新 incr 文件;子进程把 20gb 数据序列化为 base;
  4. T1:子进程完成,向父进程发送信号;父进程在下一个事件循环里改写 manifest;
  5. T1+:旧 incr 与新 base 之一被删除,aof_base_size 更新为新组合的体积。

这条时间线说明两件事。第一,阻塞只发生在 fork 那一下,之后父进程是自由的;真正的风险在于子进程写盘与父进程追加写盘同时发生,磁盘 IO 饱和会让主线程的 write 调用变慢。第二,aof_base_size 的更新发生在 T1+,如果在 T0~T1 之间再执行一次 BGREWRITEAOF,命令会被忽略(aof_rewrite_in_progress 为 1 时直接返回),不会产生第二个子进程。

六、重写期间的观测与控制

redis-cli INFO persistence | grep -E "aof_rewrite|aof_last"

关键字段:aof_rewrite_in_progress 为 1 表示正在重写;aof_last_bgrewrite_status 记录上次结果;aof_last_rewrite_time_sec 是上次耗时,大实例上可能达到数十秒。

no-appendfsync-on-rewrite yes 会在重写(以及 BGSAVE)期间暂停主 AOF 的 fsync,减少磁盘争抢,代价是这期间宕机会丢失更多数据。磁盘性能不足又要求安全的场景,保持默认 no,把预算花在更快的盘上。

常见坑 / 最佳实践

  1. 大实例把 min-size 调大。 默认 64mb 在几十 GB 的实例上会导致重写极其频繁、反复 fork;建议按数据量调到 512mb 以上,percentage 相应放宽。
  2. 磁盘按两倍 AOF 预留。 重写期间旧 incr 与新 base 并存,空间不足会让重写失败并留下垃圾文件。
  3. 不要用重写"释放内存"。 BGREWRITEAOF 只压缩日志文件,不会减少 Redis 占用的内存,两者经常被混淆。
  4. 监控重写耗时与状态。 aof_last_bgrewrite_status 非 ok 或重写时间持续变长,往往是磁盘性能退化或写入量暴涨的信号。
  5. 重写安排在低峰。 即使参数合理,fork 停顿与 IO 压力也存在,可用定时任务在凌晨执行 BGREWRITEAOF。

小结

  • AOF 只增不减,重写根据内存现状生成最小命令集,是控制文件体积与恢复时长的唯一手段。
  • Redis 7 的多部分 AOF 让新命令直接写入新 incr 文件,移除了重写缓冲区,流程更简单、内存开销更小。
  • manifest 是文件组合的登记表,base 与 incr 的搭配随每次重写轮换。
  • aof-use-rdb-preamble yes 让 base 采用 RDB 格式,恢复速度显著更快,5.0 起默认开启。
  • 重写期间磁盘占用约翻倍,容量预留、状态监控与低峰执行三件事都要落实。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。