3. 排查工具:iostat、iotop、pidstat、blktrace、fio

说到磁盘IO排查,工具就是我们的眼睛和手。没有趁手的家伙,再牛的理论也白搭。这一章我把自己压箱底的五个工具拿出来,挨个儿给你讲透。

你可能会问:一个iostat不就够了吗?说实话,不够。每个工具看问题的角度不一样。我打个比方——iostat是站在马路上看车流,iotop是盯着每辆车看谁在超速,pidstat能告诉你司机是谁,blktrace连发动机的每个冲程都能录下来,而fio嘛,是直接造一辆车去压马路。

核心思路:先宏观后微观,先定位再深挖。别一上来就开blktrace,那玩意儿数据量太大,容易把自己淹死。

3.1 iostat — 磁盘IO的“心电图”

iostat是我用得最频繁的工具,没有之一。每次接到性能告警,我第一件事就是敲iostat -x 1。为什么加-x?因为默认模式信息太少,扩展模式才能看到真正的细节。

# 每1秒刷新一次,显示扩展统计
iostat -x 1

# 只看某个设备
iostat -x sda 1

输出里字段很多,但真正关键的没几个。我列个表,你照着盯就行:

字段 含义 警戒线
%util 磁盘忙绿百分比 > 80% 要警惕,> 90% 基本饱和
r/s, w/s 每秒读写请求数 跟硬件极限比,SSD通常能到几万
r_await, w_await 平均IO等待时间(毫秒) > 10ms 算慢,> 50ms 问题严重
avgrq-sz 平均IO大小(扇区) 太小说明碎片化,太大可能批量操作
svctm 平均服务时间 接近硬件极限,SSD通常 < 1ms
我的经验:很多人只看%util,觉得没到100%就没问题。错!我遇到过%util只有60%,但应用已经卡得不行的情况。为什么?因为IO请求排队了,但磁盘本身没满。这时候要看r_await和aqu-sz(平均队列长度)。

举个例子。有一次线上MySQL写入突然变慢,我上去一看:%util 85%,w_await 45ms。嗯,磁盘确实忙,但还没到极限。再一看avgrq-sz只有8,说明每次写的数据量很小但频率极高。这明显是应用层的问题——后来发现是某个日志模块把fsync调用写进了热点路径。

3.2 iotop — 揪出“吃IO”的进程

iostat告诉你磁盘忙,但谁在搞事?iotop就是干这个的。它有点像top命令,但关注的是IO。

# 安装(CentOS)
yum install iotop

# 运行,只看实际IO的进程
iotop -oP

-o只显示有IO的进程,-P显示进程而非线程。这两个参数我每次都加,不然满屏的0看着心烦。

输出里重点关注两列:DISK READ和DISK WRITE,单位是字节/秒。还有IO>列,显示的是IO优先级。

注意:iotop需要root权限。另外,它本身会带来少量性能开销,生产环境慎用,尤其别长时间开着。

我记得有一次帮客户排查,iostat显示sdb的%util一直在95%以上。打开iotop一看,好家伙,一个叫log_aggregator的进程在疯狂写数据。一问才知道,这个进程每秒钟把几百MB的日志从各个节点拉过来再压缩写入。后来改成异步批量写入,IO直接降了70%。

3.3 pidstat — 进程级别的IO统计

pidstat是sysstat包里的,跟iostat同门。它能告诉你每个进程具体的IO行为,比iotop更精细一些。

# 查看进程IO统计,每秒刷新
pidstat -d 1

# 只看某个PID
pidstat -d -p 12345 1

关键字段:

  • kB_rd/s — 每秒读千字节数
  • kB_wr/s — 每秒写千字节数
  • cswch/s — 每秒上下文切换次数(这个很关键,IO密集往往伴随高上下文切换)

为什么有了iotop还要pidstat?因为pidstat可以跟其他统计一起看。比如我经常同时开两个窗口:一个跑pidstat -d 1,另一个跑pidstat -u 1。这样能同时看到进程的IO和CPU消耗,定位问题更准。

一个小技巧:用pidstat -d -p ALL 1 | grep -E "PID|awk"可以过滤出IO量最大的几个进程。我写了个小脚本,每10秒抓一次top5,记录到日志里。出问题时翻一翻,往往能找到规律。

3.4 blktrace — IO事件的“黑匣子”

前面几个工具都是看统计值,blktrace不一样。它能记录每个IO请求从产生到完成的完整生命周期。说白了,就是给磁盘IO装了个监控摄像头。

# 跟踪sda设备,跟踪时间10秒
blktrace -d /dev/sda -o trace -w 10

# 分析结果
blkparse trace -o trace.txt

blktrace的输出分几个阶段:

  • Q — IO请求入队列
  • G — 请求合并
  • I — 请求插入调度队列
  • D — 请求下发到驱动
  • C — 请求完成

你看,从Q到C的时间就是IO的完整延迟。如果Q到D的时间很长,说明调度器或队列有问题;如果D到C很长,那多半是硬件慢。

警告:blktrace会产生大量数据。10秒钟的跟踪,可能生成几百MB的原始数据。我建议只在问题复现时短时间开启,别当常规监控用。

有一次我遇到个诡异问题:磁盘偶尔会卡住几秒钟。iostat看不出规律,iotop也抓不到。最后用blktrace抓了30秒,发现每隔15秒左右,所有IO请求的D到C时间突然飙升到2秒以上。查了一圈,原来是磁盘固件在做内部垃圾回收。后来换了企业级SSD,问题消失。

3.5 fio — 磁盘性能的“压力测试仪”

fio不是排查工具,是测试工具。但为什么放在这里?因为排查问题时,你经常需要知道“磁盘到底能跑多快”。没有基准值,你怎么判断当前是不是慢?

# 随机读测试
fio --name=randread --ioengine=libaio --rw=randread --bs=4k --size=1G --numjobs=4 --runtime=30 --group_reporting

# 随机写测试
fio --name=randwrite --ioengine=libaio --rw=randwrite --bs=4k --size=1G --numjobs=4 --runtime=30 --group_reporting

# 混合读写
fio --name=rw --ioengine=libaio --rw=randrw --rwmixread=70 --bs=4k --size=1G --numjobs=4 --runtime=30 --group_reporting

输出里重点看:

  • IOPS — 每秒IO次数,随机读写的关键指标
  • BW — 带宽,顺序读写的关键指标
  • lat (usec) — 延迟分布,看p99和p99.9
测试场景 典型IOPS(企业级SSD) 典型延迟
4K随机读 50万 - 100万 100-300μs
4K随机写 10万 - 30万 200-500μs
128K顺序读 带宽 2-5 GB/s 延迟较低
我的习惯:每台新服务器上线前,我都会跑一套fio基准测试,把结果存下来。以后出问题时,拿当前数据跟基准一比,就知道是硬件退化还是应用负载变了。这个习惯救过我很多次。

工具选型指南

说了这么多,到底什么时候用哪个?我画了张图,帮你理清思路:

磁盘IO排查工具选型流程 收到IO性能告警 第一步:iostat -x 1 看整体 %util > 80% 或 await > 10ms? 否 检查应用层/网络 是 第二步:iotop/pidstat 找进程 第三步:blktrace 深挖 别忘了用fio跑基准测试,建立性能基线。没有基线,排查就像在黑夜里找东西。 工具选型原则:从宏观到微观,从简单到复杂,别一上来就上重武器。

总结一下我的使用习惯:

  • 日常监控:iostat + pidstat,轻量无负担
  • 定位进程:iotop快速扫描,pidstat精确抓取
  • 疑难杂症:blktrace上阵,但控制好采样时间
  • 性能评估:fio建立基线,定期对比
最后说一句:工具是死的,人是活的。别指望一个命令就能解决所有问题。我见过太多人拿着iostat的输出问我“这正常吗”——说实话,没有业务场景和基线数据,谁也说不准。所以,先把fio跑起来,把基线建起来,这才是正道。