根本原因是Linux内核page cache刷盘机制被拖慢:dirty page过多或磁盘I/O饱和时,fsync()需等待脏页落盘而阻塞数秒,导致Redis后台线程卡住、主线程延迟飙升,并触发aof_delayed_fsync持续增长。

Redis 的 fsync 阻塞根本原因不是 Redis 自身,而是 Linux 内核的 page cache 刷盘机制被拖慢——当系统 dirty page 过多、刷盘不及时,appendfsync everysec 的后台线程就会卡在 fsync() 系统调用上,进而拖累主线程响应。
为什么 fsync 会卡住:Linux dirty page 机制的真实表现
Redis 启用 AOF + appendfsync everysec 时,写命令先落 write(2) 到 page cache,再由后台线程每秒调一次 fsync() 强制刷盘。但 fsync() 实际要等内核把对应 page cache 中的 dirty 数据真正写入磁盘。如果系统 dirty page 总量太大(比如 /proc/sys/vm/dirty_ratio 接近 20%),或磁盘 I/O 持续饱和(HDD 尤其明显),fsync() 就会阻塞数秒甚至更久。
此时 Redis 日志里会出现:
* Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete...
这不是 Redis 在“异步”,而是它被迫放弃等待——后续写操作继续往 buffer 塞,但 buffer 积压会导致内存上涨、延迟飙升,严重时触发 OOM killer。
vm.dirty_ratio 和 vm.dirty_background_ratio 必须调低
默认值(如 dirty_ratio=20)对 Redis 这类高频小写场景极不友好。一旦 page cache 占满物理内存 20%,内核会强制同步刷盘,所有 fsync() 调用都会被 block。
-
vm.dirty_background_ratio设为5:当 dirty page 达到内存 5% 时,内核自动启动后台刷盘线程(pdflush或writeback),避免堆积 -
vm.dirty_ratio设为10:作为硬上限,防止突发写入打爆内存;高于此值,write(2)也会被阻塞(不只是fsync) - 修改后需运行
sysctl -p生效,且建议加到/etc/sysctl.conf永久生效
AOF 配置必须配合内核参数调整
只调内核参数不改 Redis 配置,效果有限;只改 Redis 配置不调内核,等于治标不治本。
- 确认
appendfsync是everysec(不是always,否则每次写都卡主线程;也不是no,丢失数据风险高) - 开启
no-appendfsync-on-rewrite yes:AOF 重写期间暂停fsync,避免重写 + 刷盘双重压力 - 禁用
linux-kernel-transparent-hugepage:hugepage 会加剧 dirty page 分配和回收延迟,执行echo never > /sys/kernel/mm/transparent_hugepage/enabled - 避免在 Redis 机器上跑其他高 IO 进程(如备份脚本、日志轮转),它们会抢占
writeback资源
如何验证是否真由 dirty page 导致
别只看 Redis 日志,直接查内核状态:
- 实时看 dirty page 占比:
cat /proc/meminfo | grep -i dirty,关注Dirty:和Writeback:行 - 看刷盘压力:
cat /proc/vmstat | grep -E "pgpgout|pgpgin|pswpout|pswpin|pgmajfault",若pgpgout持续 > 50MB/s 且Writeback长时间不降,说明刷盘已跟不上 - 检查磁盘实际 util:
iostat -x 1,观察%util是否长期 > 90%,await是否 > 50ms(HDD 场景常见) - 对比
INFO PERSISTENCE中aof_delayed_fsync计数器——该值持续增长,就是fsync被迫跳过的铁证
真正棘手的是:这些参数之间存在隐式耦合。比如调低 dirty_ratio 后,若磁盘本身太慢(如老旧 HDD),writeback 线程仍会积压,只是从“全卡死”变成“缓慢爬行”。这时候换 SSD 不是可选项,是必选项。


















