iostat -x 1 才能看清真实 i/o 压力,%util 接近 100% 不代表磁盘堵死,需关注 await、avgqu-sz 及 r_await/w_await;iotop -a 可显示内核线程 i/o;调优前先用 strace 定位系统调用根源。

用 iostat 看清真实 I/O 压力,别只盯着 %util
很多人看到 %util 接近 100% 就以为磁盘堵死了,其实它只是设备忙时间占比,不反映请求排队长度或响应延迟。真正卡顿往往发生在 await(平均等待毫秒数)飙升、avgqu-sz(平均队列深度)持续大于 1 的时候。
-
iostat -x 1是必须加的参数,不带-x看不到扩展指标 - 重点关注
r_await/w_await,超过 10ms 就值得查,SSD 超过 20ms、HDD 超过 50ms 通常已异常 -
svctm在 2.6.33+ 内核已被弃用,显示为 0 或不准,别信它 - 如果
avgqu-sz长期 > 队列深度(cat /sys/block/sda/queue/nr_requests默认 128),说明内核 I/O 调度器在积压请求
iotop 定位具体进程,但默认看不到内核线程 I/O
iotop 默认过滤掉内核线程(如 kswapd、khugepaged),而它们可能正在疯狂刷脏页或做透明大页回收,导致 I/O 暴涨却找不到“罪魁祸首”。
- 启动时加
-a参数:iotop -a,才能显示所有线程,包括内核线程 - 按
P切换到“累计 I/O”视图,更容易发现长期吃 I/O 的后台任务 - 注意区分
IO>0和IO=0进程——后者可能是阻塞在 I/O 完成上,而非发起 I/O - 如果看到
kswapd0或ksmd占高,大概率是内存不足触发了交换或内存压缩,本质是内存问题,不是磁盘问题
调整 I/O 调度器前,先确认设备类型和负载模式
盲目把 cfq(旧版)或 mq-deadline 换成 none(即 kyber 或 bfq)不一定更快,反而可能让随机小 IO 延迟翻倍。
- NVMe 设备建议用
none(绕过调度器),但要确认内核 >= 5.0;老内核用kyber - SATA SSD 用
mq-deadline更稳,bfq在高并发小 IO 场景下延迟更可控,但吞吐略低 - HDD 必须用
mq-deadline或bfq,none会让寻道完全无序,性能断崖下跌 - 改调度器:写入
echo kyber > /sys/block/nvme0n1/queue/scheduler,重启不保留,需写入/etc/default/grub的GRUB_CMDLINE_LINUX加nvme_core.default_ps_max_latency_us=0等配套参数
vm.dirty_ratio 和 vm.dirty_background_ratio 是写缓存关键开关
这两个值控制内核何时开始异步刷脏页(background)和何时强制同步阻塞写(ratio)。设太高会导致突发写入时系统卡死,太低又浪费 I/O 吞吐。
- 默认
dirty_ratio=20(占内存 20%),对 64G 内存机器就是 12.8G 脏页才阻塞,非常危险 - 生产环境建议调低:
vm.dirty_background_ratio=5,vm.dirty_ratio=10,配合vm.dirty_expire_centisecs=3000(30 秒过期) - 修改后立即生效:
sysctl -w vm.dirty_background_ratio=5,但要写入/etc/sysctl.conf避免重启失效 - 注意:容器环境里这些是 host 全局参数,不是 per-container,Kubernetes Pod 无法单独隔离
strace -e trace=write,fsync,openat -p $PID 抓一把系统调用,比调 scheduler 管用十倍。











