bigywater.tech
Redis · 65

no-appendfsync-on-rewrite

AOF 重写时,子进程在写大量数据,主进程如果还在频繁 fsync,两者会抢磁盘。no-appendfsync-on-rewrite 的意思是:重写期间主进程暂停 fsync。它到底有没有用?这篇用 200 万个 key、单客户端持续写入,把重写前、中、后的延迟分段量出来,每种配置跑两轮。

2.2 ms → 30 µs
always 下重写期间 SET 的 p50:开关 no → yes
7.6 s → 1.1 s
同样两种配置下 AOF 重写自身的耗时
无差别
everysec 下这个开关对延迟没有明显影响

做法

每种配置一个新实例:装入 200 万个 key,然后一个客户端不停地同步 SET(记录每条的往返时间);1 秒后另一个连接发出 BGREWRITEAOF,直到 aof_rewrite_in_progress 回到 0,再多记 1 秒。把所有 SET 按时间分成「重写前 / 中 / 后」三段,各段统计条数、p50、p99、最大值。四种组合:appendfsync always / everysec × no-appendfsync-on-rewrite no / yes,各跑两轮。

结果

单位 µs;「轮」是第几轮
实测 · 本机执行输出
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」下的结论未必仍然成立。

分步演示:开关的效果

no-appendfsync-on-rewrite
本步的数据

    本文用到的工具与边界

    • 本机 macOS 27 / Redis 8.10.2 独立实例(本机回环端口,save 关闭),redis-cli 风格输出由自写的 RESP 客户端打印;计时用 time.perf_counter,数字每次运行会有小幅抖动。每种配置两轮,表中是每轮各阶段的 p50 / p99 / max;文中的 p50 等是两轮的中位
    • 没有验证:断电时重写期间写入的丢失量、Linux 与其它磁盘上的表现、多个客户端并发写时的表现、AOF 重写与 RDB 保存同时进行的情形、「always 不跳过」时重写变慢的具体原因
    京ICP备2026067502号-1京公网安备11010502063487号