Redis 的 AOF 持久化记录的是每次写命令,随着时间推移文件体积会超过实际数据集大小。为了收敛体积,Redis 提供了 BGREWRITEAOF,它并不是把旧 AOF 文件压缩,而是 fork 一个子进程,直接根据内存中的当前数据生成一份新的 AOF。这个过程恰好是容易引起性能毛刺的地方。优化 BGREWRITEAOF 不是为了让它更快地写文件,而是让它在后台完成工作的同时尽量不影响主进程响应客户端。

一、理解 BGREWRITEAOF 的资源模型
BGREWRITEAOF 触发后,Redis 主进程会调用 fork 创建子进程。Linux 下的 fork 采用写时复制机制,也就是说刚 fork 出来时父子进程共享相同的物理内存页,只有在某一方发生写入时,内核才会复制对应的内存页。对于 Redis 这种内存数据库,如果实例写入很频繁,fork 之后主进程的每个写操作都会导致一部分内存页被复制,从而增加内存占用。如果系统没有启用 overcommit_memory 或预留不足,fork 可能直接失败。
子进程的任务是遍历当前数据集,把每个键按照 Redis 的序列化格式写入临时文件。这里主要的开销是 CPU 和磁盘顺序写。如果数据量很大,子进程会持续占用一个 CPU 核心并产生大量磁盘 IO,可能和主进程的 IO 请求争抢。重写完成前,主进程会继续接收命令,并将这些命令同时写入 AOF 缓冲区和重写缓冲区。当子进程完成基础数据写入后,主进程把重写缓冲区中的增量命令发送给子进程,子进程追加到临时文件末尾,最后原子地替换旧的 AOF 文件。
由此可见,重写过程涉及三方面压力:fork 时的内存页表复制、子进程写文件时的 CPU 与磁盘竞争、重写最后阶段主进程发送缓冲区的管道消耗。很多优化手段都是围绕这三点展开。
二、调整触发阈值与磁盘同步策略
Redis 有两个参数决定自动触发重写的时机:auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size。前者表示当前 AOF 文件大小相比上一次重写后的增长百分比,后者是触发重写的最低文件大小。例如以下配置:
# AOF文件超过64MB后,若比上次重写后增长100%则触发重写 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb
如果 min-size 设置得太小,会导致频繁重写;如果 percentage 过大,AOF 文件会在重写前膨胀到很大,增加磁盘压力和恢复时间。一般建议将 min-size 设置在实例日常 AOF 大小的 2 到 3 倍左右,percentage 保持默认 100 即可。对于写入非常密集的场景,可以把 percentage 调到 200,减少重写次数,但要注意磁盘空间。
另一个关键配置是 no-appendfsync-on-rewrite。AOF 持久化通常依赖 fsync 刷盘策略,默认 everysec 每秒刷一次。重写期间子进程已经产生大量磁盘 IO,如果主进程仍然每秒 fsync,两者会争抢磁盘,导致 Redis 响应延迟增高。将该参数设为 yes 后,主进程在重写期间不会主动调用 fsync,只依赖操作系统刷盘。这样能显著降低延迟抖动,副作用是最多丢失重写期间的部分写入记录,通常可以接受。
# 有BGSAVE或BGREWRITEAOF执行时,主进程不执行fsync no-appendfsync-on-rewrite yes
另外 aof-use-rdb-preamble 建议开启。它让重写后的 AOF 文件开头使用 RDB 二进制格式,之后才是增量 AOF 命令。这样文件体积更小,Redis 重启加载时也更快。混合格式虽然不再是纯文本,但 Redis 会自动识别,日常使用没有兼容性问题。
# 开启AOF文件混合持久化 aof-use-rdb-preamble yes
还可以配合 aof-rewrite-incremental-fsync yes,让子进程在重写过程中分批刷盘而不是最后一次性刷盘,避免重写完成时出现磁盘 IO 高峰。
三、监控重写状态与异常排查
优化不能只靠配置,还需要观察重写过程是否健康。执行 redis-cli info Persistence 可以看到几个关键指标。例如:
redis-cli info Persistence | grep aof_ aof_enabled:1 aof_rewrite_in_progress:0 aof_last_bgrewrite_status:ok aof_current_size:134217728 aof_base_size:67108864
其中 aof_rewrite_in_progress 为 1 表示重写正在进行,aof_last_bgrewrite_status 为 err 说明上一次重写失败,需要结合日志查看原因。如果 aof_current_size 一直远大于 aof_base_size,但是重写迟迟没有触发,可能意味着 percentage 阈值配置不合理,或者手动触发命令没有执行成功。
当重写失败时,常见原因是 fork 时内存不足。Linux 的 vm.overcommit_memory 如果设置为 0,fork 在可用内存不足时可能返回错误。建议设置为 1,并适当预留 swap。另一个原因是磁盘空间不足,子进程写临时文件失败。可以通过 dir 目录所在分区的使用率判断。重写期间的延迟毛刺还可以通过 Redis 的 LATENCY DOCTOR 或 SLOWLOG 命令辅助分析。
手动触发 BGREWRITEAOF 的命令是 redis-cli BGREWRITEAOF,适合在低峰期主动整理 AOF 文件。但要避免主进程已经处于 fork 子进程状态时再次触发,因为 Redis 不会并行重写,新的命令会被忽略或排队。总之,通过合理的触发阈值、磁盘同步策略和监控手段,可以让 BGREWRITEAOF 在后台安静地完成 AOF 体积收敛。
RedisBGREWRITEAOFAOF重写修改时间:2026-09-25 16:55:37