
怎么从 /proc/diskstats 读出真实 IO 负载
Linux 的 /proc/diskstats 不是“每秒多少次读写”的快照,而是自启动以来的累计计数。直接差值计算前必须确认采样间隔和设备对齐——否则你会看到负数、突增或完全不更新的数值。
- 每次读取后要 sleep 至少
100ms,短于这个时间差值常为 0(内核更新频率限制) - 同一行字段顺序固定但列数可能不同:老内核 11 列,新内核(5.10+)14 列,务必用空格切分 + 跳过空字符串,别硬按索引取第 4/5/9 列
- 设备名可能带前缀:
sda和nvme0n1是主流,但 LVM 或 multipath 下会出现dm-0,需结合/sys/block/*/dev反查主设备号/次设备号来去重 - 示例片段:
8 0 sda 123456 123 2345678 45678 98765 456 876543 23456 0 34567 89012
其中第 4 列(2345678)是读完成次数,第 8 列(876543)是写完成次数,第 13 列(34567)是毫秒级读等待总时长
os.Open("/proc/diskstats") 为什么偶尔 panic: no such file or directory
/proc 是虚拟文件系统,路径存在性依赖内核挂载状态和进程命名空间。容器环境、chroot 或某些精简发行版(如 Alpine 默认不挂 /proc)会导致该路径不可见。
- 永远用
os.Stat先检查/proc/diskstats是否可访问,不要假设它一定存在 - 在容器中运行时,确保启动参数包含
--privileged或至少挂载/proc:/proc:ro - 如果
Stat失败,退而尝试读/sys/block/*/stat—— 每个块设备一个文件,格式更简单(纯数字空格分隔),但需遍历目录并过滤掉loop、ram等伪设备
用 bufio.Scanner 读 /proc/diskstats 卡死或丢行?
/proc 文件没有 EOF 语义,部分内核版本返回数据末尾不带换行符,Scanner.Scan() 会阻塞等待下一行或超时失败。
- 改用
io.ReadAll+strings.Split更可靠,/proc/diskstats通常小于 10KB,内存开销可忽略 - 若坚持用
Scanner,必须设ScanLines模式并手动处理最后一行无\n的情况(检查Scanner.Err()是否为io.EOF) - 别用
Scanner.Text()直接赋值给结构体字段——某行开头有空格时会截断设备名(如" 8 0 sda"→"sda"正确,但误切可能得"0")
监控指标选 io_ticks 还是 weighted_io_ms?
io_ticks(第 10 列)是设备忙的时间(单位:jiffies),受系统 HZ 影响;weighted_io_ms(第 13 列)是加权毫秒数,已按 I/O 队列长度加权,更适合反映真实负载压力。
立即学习“go语言免费学习笔记(深入)”;
- 做容量评估用
weighted_io_ms:它体现“有多少请求在排队等服务”,数值高说明磁盘成为瓶颈 - 做延迟分析别只看这个:它不区分读/写,也不含队列等待之外的耗时(如存储网络延迟)
- 注意单位一致性:
io_ticks需除以user_hz(可用syscall.Sysinfo获取)转成毫秒;weighted_io_ms本身就是毫秒,直接差值即可
真正难的是把瞬时差值映射到业务影响上——比如 weighted_io_ms 一秒钟涨了 2000ms,到底是 DB 在 bulk insert,还是 backup 进程锁住了整个卷。得结合 /proc/PID/io 和 lsof -p 实时反查,这部分没法靠单点读 /proc 解决。










