老版本的 AOF 重写靠一块「重写缓冲区」:子进程写快照的同时,主进程把新命令攒在内存里,最后追加进新文件。这块缓冲区是重写期间内存暴涨的常见原因。Redis 7 之后的多文件 AOF 据说不再需要它。这篇不凭「据说」,而是在重写进行的那一秒里,每毫秒看一次 AOF 目录,再用 kill -9 验证有没有丢写。
aof_enabled, aof_rewrite_in_progress, aof_rewrite_scheduled, aof_last_rewrite_time_sec, aof_current_rewrite_time_sec, aof_last_bgrewrite_status, aof_rewrites, aof_rewrites_consecutive_failures, aof_last_write_status, aof_last_cow_size, aof_current_size, aof_base_size, aof_pending_rewrite, aof_buffer_length, aof_pending_bio_fsync, aof_delayed_fsync 包含 "buffer" 的字段: ['aof_buffer_length']
Redis 8 的 INFO persistence 里以 aof_ 开头的字段列在上面。包含 buffer 的只有 aof_buffer_length——从名字看是普通写入路径上的缓冲(我没有去验证它的确切含义)。没有类似 aof_rewrite_buffer_length 的字段(老版本的 INFO 里有,这一点来自我的记忆,本文没有用老版本验证)。
重写开始前的文件:[('appendonly.aof.1.base.rdb', 89), ('appendonly.aof.1.incr.aof', 201923341)]
+ 0.02s aof_rewrite_in_progress=1 文件:[('1.base.rdb', 89), ('1.incr.aof', 201923341), ('2.incr.aof', 253)]
+ 0.95s aof_rewrite_in_progress=0 文件:[('2.base.rdb', 33388996), ('2.incr.aof', 908477)]
重写共耗时约 0.95 s;期间写入线程共完成 INCR 86125 次(重写前 0.5 s 已经在写)
最终 manifest:
file appendonly.aof.2.base.rdb seq 2 type b
file appendonly.aof.2.incr.aof seq 2 type i startoffset 1523236发出 BGREWRITEAOF 的瞬间(+0.02 s)目录里多了 2.incr.aof,旧的 1.incr.aof 停在 201923341 字节不再变化——新的写入都去了 incr.2。约 0.95 s 后重写结束,目录只剩 2.base.rdb(33.4 MB)和 2.incr.aof(908477 字节)。重写期间的写入直接落进新的 incr 文件,不需要在内存里再攒一份。所以重写期间额外占用内存的主要是 fork 的写时复制(COW),不是缓冲区(COW 的实测见第 66 篇)。
写线程确认的 INCR 次数 = 86125;kill -9 前 seq = 86125;重启后从 AOF 恢复的 seq = 86125;dbsize = 1500001 重启加载耗时约 0.95 s
为了验证,写线程每次 INCR seq 收到回复才计数。重写结束后再等 1.3 秒让 everysec 的 fsync 落盘,kill -9 主进程,重启并等加载完成(约 1 秒):恢复的 seq = 86125,与确认次数 86125、kill 前的 86125 完全一致。注意这是「进程被杀」的场景,操作系统还在,页缓存里的数据不会丢;断电级别的丢失没有测(第 64 篇讨论 fsync 策略)。