iostat -x 1 无法直接识别同步写入瓶颈,需结合 w_await 高、wkB/s 低、avgrq-sz 极小(常

iostat -x 1 看不到同步写入瓶颈?先确认是否真在落盘
同步写入(如 fsync、fdatasync、O_SYNC)导致的性能下降,不会直接出现在 iostat -x 1 的“吞吐量”里——它可能表现为高 r_await/w_await、低 rkB/s/wkB/s、但 avgrq-sz 极小(常 %util 不高却响应卡顿。根本原因是:同步写不等于“马上写磁盘”,而是“等落盘完成才返回”,所以瓶颈常卡在队列或设备响应上,而非带宽耗尽。
- 运行
iostat -xz 1,重点盯:w_await(写延迟)、aqu-sz(平均队列长度)、avgrq-sz(平均请求大小);若w_await > 20ms且avgrq-sz ,大概率是小块同步写堆积 -
%util低 ≠ 没压力:NVMe 或多队列设备上,%util可能只反映单个队列忙时,实际aqu-sz > 1就说明有排队 - 别信
svctm:内核已废弃,数值无意义,w_await - svctm的差值才是排队等待时间,但svctm本身不准,直接看w_await和aqu-sz更可靠
iotop -o 只显示“真干活”的进程,但默认不显同步行为
iotop -o 能定位到哪个进程在刷盘,但它显示的是“当前 I/O 带宽”,不是“是否同步”。Java、PostgreSQL、MySQL 等默认大量使用 fsync,但 iotop 无法区分它是缓存写还是同步落盘。
- 必须加
-o(只显示有 I/O 活动的进程),否则混入休眠线程,关键进程被淹没 - 看
IO>列(KB/s),不是IO%:后者是相对占比,对判断绝对压力没用 - 查到高 IO 进程后,立刻执行:
lsof -p PID | grep -E "(REG|CHR)",重点关注日志文件(pg_wal/、mysql-bin.*、/var/log/journal/)和数据库数据文件——这些路径上的写操作最可能触发同步
/proc/PID/io 里 read_bytes/write_bytes 才是真实落盘量
/proc/PID/io 是唯一能区分“系统调用层”和“物理落盘层”的接口。其中 write_bytes 是真正写到磁盘的字节数,而 wchar 是进程发起的所有写系统调用字节数。两者差值越大,说明越依赖页缓存,同步写越少;差值接近 0,则几乎每次写都在等落盘。
- 执行:
cat /proc//io | grep -E "(write_bytes|wchar)" - 若
write_bytes ≈ wchar(比如相差 O_SYNC 或强制fsync - 若
write_bytes (比如只有 10%),说明大部分写还在 Page Cache,此时iostat看到的 I/O 是延迟发生的,不是实时瓶颈 - 注意:
write_bytes单位是字节,不是扇区;/proc/diskstats第 10 列才是写扇区数,需 ×512 换算
查同步写源头:从应用配置和内核参数切入
同步写不是凭空出现的,要么来自应用代码(fsync() 调用),要么来自文件系统挂载选项(data=ordered、barrier=1),或是数据库 WAL 设置。盲目优化没用,得先锁定来源。
- 检查挂载选项:
findmnt -D | grep -E "(barrier|sync|data=)",重点关注data=journal(ext4)或commit=5(影响刷盘频率) - 查 PostgreSQL:
show synchronous_commit;(on表示每事务都 fsync WAL)、wal_sync_method(fsync最慢,open_sync稍快) - 查 MySQL:
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';(=1 表示每次 commit 都刷盘) - 查 systemd-journald:
grep -i "storage" /etc/systemd/journald.conf,Storage=persistent+ 未设SystemMaxUse会导致持续同步刷日志
同步写性能问题最难的不是发现,而是确认它是否真的必要——很多场景下,关掉 fsync 或改用异步刷盘(如 PostgreSQL 的 synchronous_commit=off)能立竿见影,但会牺牲部分持久性。别只盯着工具输出的数字,先问一句:这个同步,到底保的是哪条命?











