AOF 重写时,子进程在写大量数据,主进程如果还在频繁 fsync,两者会抢磁盘。no-appendfsync-on-rewrite 的意思是:重写期间主进程暂停 fsync。它到底有没有用?这篇用 200 万个 key、单客户端持续写入,把重写前、中、后的延迟分段量出来,每种配置跑两轮。
每种配置一个新实例:装入 200 万个 key,然后一个客户端不停地同步 SET(记录每条的往返时间);1 秒后另一个连接发出 BGREWRITEAOF,直到 aof_rewrite_in_progress 回到 0,再多记 1 秒。把所有 SET 按时间分成「重写前 / 中 / 后」三段,各段统计条数、p50、p99、最大值。四种组合:appendfsync always / everysec × no-appendfsync-on-rewrite no / yes,各跑两轮。
appendfsync on-rewrite 轮 重写耗时 阶段 条数 p50 p99 max always no 1 6.57s 前 499 1904 3598 7668 always no 1 6.57s 中 3412 1435 3582 19358 always no 1 6.57s 后 364 2986 4086 9359 always yes 1 1.09s 前 497 1528 3632 9329 always yes 1 1.09s 中 32997 30 64 14880 always yes 1 1.09s 后 372 2973 3253 3905 everysec no 1 1.01s 前 31557 29 57 902 everysec no 1 1.01s 中 31507 30 52 13857 everysec no 1 1.01s 后 33250 29 45 215 everysec yes 1 1.02s 前 30974 30 70 816 everysec yes 1 1.02s 中 30973 31 51 12265 everysec yes 1 1.02s 后 33207 29 43 195 always no 2 8.65s 前 340 2999 3499 4614 always no 2 8.65s 中 3302 2962 3986 33018 always no 2 8.65s 后 329 3000 4578 9068 always yes 2 1.07s 前 628 1337 3297 7697 always yes 2 1.07s 中 32335 30 67 12905 always yes 2 1.07s 后 382 2952 3519 4458 everysec no 2 1.03s 前 31578 29 67 700 everysec no 2 1.03s 中 31369 30 51 13456 everysec no 2 1.03s 后 33531 29 41 257 everysec yes 2 1.02s 前 32705 29 66 389 everysec yes 2 1.02s 中 32141 29 54 11606 everysec yes 2 1.02s 后 33929 28 42 242
always + 不跳过(默认):重写期间每条 SET 仍在等 fsync,p50 2.20 ms;更值得注意的是重写本身也慢了——两轮分别是 6.6 s 和 8.6 s,而其它三种配置都只要约 1.0–1.1 s。重写期间只写进 3357 条 SET(其它配置写了三万多条)。为什么慢这么多?我的猜测是主进程的高频 fsync 和子进程的大量写盘在抢磁盘,但我没有做隔离实验去证明。
always + 跳过:重写期间主进程不 fsync,SET 的 p50 降到 30 µs,p99 66 µs,与 everysec 相当,重写只要 1.08 s;重写一结束又回到每条 fsync(「后」阶段 p50 回到 3 ms 左右)。
everysec:开不开关差别不大:p50 都是约 30 µs,重写都是约 1.0 s;最大延迟都是 10 ms 量级——那是 fork 本身的停顿,和 fsync 策略无关(第 66 篇)。
开关设成 yes 的代价是:重写期间「已经回复给客户端」的写入没有 fsync,这段时间里操作系统崩溃或断电会丢数据(丢多少取决于操作系统的写回策略)。这个后果我没有实测——同第 64 篇,我没有能力在这台机器上制造断电。实验也只用了单客户端、200 万个 key、Apple SSD;数据集更大、重写更久时,「always + 不跳过」的问题会更明显,「everysec」下的结论未必仍然成立。