1. 现象:业务卡顿,日志刷屏

某个数据库集群在晚间高峰期出现周期性延迟飙升,应用侧平均响应时间从 30ms 涨到 2s 以上。 第一直觉是 CPU 不够,但 top 一看负载并不高。真正的嫌疑指向磁盘。

排查 IO 问题的第一步,永远是用数据确认「确实是磁盘慢」,而不是凭感觉换硬件。

2. 第一步:iostat 看全局

iostat -x 1 每秒采样,重点关注以下指标:

$ iostat -x 1 5
Device     r/s     w/s   rkB/s   wkB/s  rrqm/s  wrqm/s  %rrqm  %wrqm
vda       12.00  210.00  1024.00  56320.00   3.00  180.00   20.0   46.2
                r_await  w_await  aqu-sz  rareq-sz  wareq-sz  svctm  %util
                  8.20    96.40   8.75     85.33   268.19  10.55   100.0

关键解读:

注意 %util 100% 并不直接等于性能瓶颈。例如一块 NVMe SSD 单队列也能到 100%,但仍有大量余量。 判断依据是「延迟是否随之恶化」,而非单纯看利用率。

3. 第二步:pidstat 定位进程

确认磁盘慢之后,需要知道「是谁在写」。用 pidstat -d 1 看每个进程的 IO:

$ pidstat -d 1
12:30:01    UID   PID   kB_rd/s  kB_wr/s  kB_ccwr/s  iodelay  Command
12:30:02      0   1832      0.00    51200.00       0.00     380  mysqld
12:30:02      0   2441      0.00      800.00       0.00       5  journald

结果一目了然:mysqld 每秒写入 50MB 且 iodelay 高达 380ms。 到这里基本锁定是数据库写入引发,接下来确认是业务查询还是后台刷脏页。

$ sudo iotop -bk -d 1
Total DISK READ:  0.00 B/s | Total DISK WRITE:  51.20 MB/s
  TID  PRIO  USER  DISK READ  DISK WRITE  IO    COMMAND
 1832 be/4  mysql    0.00 B     51.00 M  100%  mysqld
技巧 iostat 回答「盘慢不慢」,pidstat -d 回答「谁在慢」。 两者配合,一分钟内就能完成第一轮定位。

4. 第三步:用 fio 验证盘的真实能力

怀疑是存储层问题(例如云盘 IOPS 被限流、磁盘老化)时,用 fio 做基准测试。 用与业务相近的 IO 模式(本例为随机写、4K)进行压测:

$ fio --name=randwrite \
       --ioengine=libaio --direct=1 \
       --rw=randwrite --bs=4k --iodepth=32 \
       --size=1G --numjobs=1 --runtime=30 \
       --group_reporting
  write: IOPS=3820, BW=14.9MiB/s (15.6MB/s)
        lat (usec): min=115, max=15871, avg=8205.46, stdev=4321.2
        clat percentiles (usec):
         99.00th=[14746], 99.90th=[15843], 99.99th=[15843]

测试结论:

顺带验证顺序写作为对照:

$ fio --name=seqwrite --ioengine=libaio --direct=1 \
       --rw=write --bs=1M --iodepth=16 \
       --size=4G --runtime=30 --group_reporting
  write: IOPS=4200, BW=4.1GiB/s (4.4GB/s)
        lat (avg): 162.44 usec

5. 常见根因与对策

磁盘 IO 问题的根因通常落在以下四类,对照处置即可:

根因 症状 对策
IOPS 配额不足 fio 实测低于承诺值 升级云盘类型或容量
日志写入风暴 journald 写量飙升 调整日志级别/轮转
数据库刷脏页 定期写峰值 调大 buffer_pool、错峰
文件系统碎片/超配 长期使用后变慢 迁移、碎片整理

回到本文的案例:最终通过提升云盘 IOPS 配额 + 调大 MySQL innodb_buffer_pool_size 减少落盘频率,晚高峰延迟恢复到 30ms 基线。

方法论总结 从 iostat 看全局 → pidstat/iotop 定位进程 → fio 验证硬件能力 → 对照根因处置。 用这套流程,大多数磁盘性能问题都能在一个小时内找到答案。

← 返回首页