问题出现得很突然,日志倒是留得挺全。阿里云 ESSD 盘上跑图片站,运维照网上教程把 read_ahead_kb 从 128 调到 4096,结果缩略图接口的 P99 从 30ms 涨到 90ms,顺序读却没快多少。

刚开始盯着「预读值按设备设置,重启或者换盘符后自动丢失,前后对比不在同一状态」,折腾了半个多小时,排掉了。

真正麻烦的地方在于,几个原因的现象几乎一样。预读窗口过大,每次请求都多读几十倍的数据,把缓存和带宽浪费在不会被用到的块上。另一种情况是图片访问是随机小 IO,预读命中率极低,反而挤掉了真正需要的缓存页。

还有一种情况,fadvise 和预读同时生效,两者叠加导致实际读取量翻倍。

动手时先确认当前值和设备路径

blockdev --getra /dev/vdb && lsblk -d -o NAME,ROTA

回到默认值做一次对照测试

blockdev --setra 128 /dev/vdb

接着用带预读提示的随机读验证效果

fio --name=randread --filename=/data/test.img --rw=randread --bs=4k --iodepth=32 --runtime=60 --time_based

剩下的就是把确认有效的值固化成 udev 规则

printf 'ACTION=="add|change", KERNEL=="vdb", ATTR{queue/read_ahead_kb}="128"\n' > /etc/udev/rules.d/60-io.rules

别只看报错消失,验证要多做一步。fio 的 randread 结果中 iops 回到调整前水平、clat p99 低于 40ms,图片接口 P99 回落,且重启后 blockdev –getra 仍是设定值。

容易忽略的是,预读只对顺序读友好,数据库和图片站这类随机负载要保持较小值;网上给的 4096 是机械盘时代的经验,云盘上直接照搬会伤性能。同一份配置在测试环境跑通,不代表线上没事,两边的默认值经常不一样。

作者 9823189