计算机有三大件:CPU、内存、磁盘,这三者有一个拖后腿的,那就是磁盘。在生产环境,作为数据库角色的服务器磁盘建议拿至少4块硬盘做Raid10,这样既保证数据读写速度也保证数据的安全。如果使用普通的磁盘,即使CPU再强悍,最终的服务器性能也不会太好。

经常有遇到因为磁盘I/O效率低而导致MySQL查询非常慢的问题。对于一般的小网站来说,MySQL的查询队列不会超过100个,甚至不会超过10个,这是因为MySQL查询速度非常快。如果查询队列数量突然变大,可能是因为网站访问量变大也可能是因为磁盘读写速度变慢。

本案例的背景是,一台阿里云机器,收到告警磁盘IO达到100%,但是登录机器后查看并没有什么异常,也就是说磁盘飙到100%只是短暂的一会儿。既然出现了100%的情况,那说明肯定是有某个进程有问题。由于这个问题并不是一直出现,所以排查起来有点困难。于是,想到写一个监测脚本,来实时查看磁盘IO使用情况,当发现异常时,则通过一些查看服务器状态的指令来记录具体的指标,从而分析出是什么造成的磁盘IO使用率100%。


知识点一:使用iostat查看磁盘IO

如果没有iostat命令,需要安装sysstat包。

iostat不加任何选项:

# iostat
Linux 3.10.0-693.el7.x86_64 (wbs) 	2019年08月20日 	_x86_64_	(2 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           0.07    0.00    0.21    0.01    0.00   99.71

Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
sda               0.28         0.69         1.89     326317     891105
sdb               0.00         0.01         0.00       4332          0
scd0              0.00         0.00         0.00       1028          0
dm-0              0.00         0.00         0.00       1036          0

avg-cpu:为总体CPU使用统计情况,对于多核CPU,这里为所有CPU的平均值。

Device:为各磁盘设备的io统计信息。


对于CPU统计信息一行,我们主要看iowait的值,它指示CPU用于等待io请求完成的时间。

Device中各列含义如下:

Device:设备名称
tps:每秒进程下发的IO读、写请求数量
Blk_read/s:每秒读扇区数量(一扇区为512bytes)
Blk_wrtn/s:每秒写扇区数量
Blk_read:取样时间间隔内读扇区总数量
Blk_wrtn:取样时间间隔内写扇区总数量

我们经常会在iostat后加上两字数字,如:

# iostat 1 3

表示每隔1秒打印1次,共打印3次。iostat命令还有一个非常实用的选项-x,它可以显示更多的信息,也是一个最常用的选项:

# iostat -d -x 1 2
Linux 3.10.0-693.el7.x86_64 (wbs) 	2019年08月21日 	_x86_64_	(2 CPU)

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     0.02    0.01    0.27     0.68     1.90    18.11     0.00    6.45   24.55    5.48   1.94   0.06
sdb               0.00     0.00    0.00    0.00     0.01     0.00    46.33     0.00    9.07    9.07    0.00   6.27   0.00
scd0              0.00     0.00    0.00    0.00     0.00     0.00   114.22     0.00   51.11   51.11    0.00  30.56   0.00
dm-0              0.00     0.00    0.00    0.00     0.00     0.00    48.19     0.00    4.02    4.02    0.00   3.67   0.00

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
sdb               0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
scd0              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
dm-0              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00

说明:-d选项可以把CPU相关信息过滤掉,只显示磁盘相关信息,以下为各列的含义:

rrqm/s:每秒对该设备的读请求被合并次数,文件系统会对读取同块(block)的请求进行合并。
wrqm/s:每秒对该设备的写请求被合并次数。
r/s:每秒完成的读次数。
w/s:每秒完成的写次数。
rkB/s:每秒读数据量(kB为单位)。
wkB/s:每秒写数据量(kB为单位)。
avgrq-sz:平均每次IO操作的数据量(扇区数为单位)。
avgqu-sz:平均等待处理的IO请求队列长度。
await:平均每次IO请求等待时间(包括等待时间和处理时间,毫秒为单位)。
svctm:平均每次IO请求的处理时间(毫秒为单位)。
%util:采用周期内用于IO操作的时间比率,即IO队列非空的时间比率。

对于这些列,我们最应该关注的是%util,本案例中提到磁盘使用率100%,其实就是%util的值为100%。


知识点二:iotop查看哪个进程磁盘读写最高

iotop命令是一个用来监视磁盘I/O使用状况的top类工具。iotop具有与top相似的UI,其中包括PID、用户、I/O、进程等相关信息。iotop命令就是由iotop包安装得来的。

iotop结果显示跟top类似,它是动态实时查看各个进程的磁盘读写情况,效果如下:

Total DISK READ :	0.00 B/s | Total DISK WRITE :       0.00 B/s
Actual DISK READ:	0.00 B/s | Actual DISK WRITE:       0.00 B/s
   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND                                                               
 45078 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.09 % [kworker/0:0]
     1 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % systemd --switched-root --system --deserialize 21
     2 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [kthreadd]
     3 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [ksoftirqd/0]
     5 be/0 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [kworker/0:0H]
     7 rt/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [migration/0]
   520 be/0 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [xfs-data/sda1]
     9 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [rcu_sched]
    10 rt/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [watchdog/0]
    11 rt/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [watchdog/1]
    12 rt/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [migration/1]
    13 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [ksoftirqd/1]
   526 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [xfsaild/sda1]
    15 be/0 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [kworker/1:0H]
   528 be/0 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [kdmflush]
    17 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [kdevtmpfs]
    18 be/0 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [netns]
    19 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [khungtaskd]
    20 be/0 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [writeback]
    21 be/0 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [kintegrityd]
    22 be/0 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [bioset]
    23 be/0 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [kblockd]
    24 be/0 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [md]
  1049 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % gssproxy -D
  1050 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % gssproxy -D
  1052 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % gssproxy -D
  1053 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % gssproxy -D

主要看第4列和第5列。iotop有几个快捷键,如下:

左右方向键:改变排序方式,默认是按IO排序。

r:改变排序顺序。

o:只显示IO输出的过程。

p:进程/线程的显示方式的切换。

a:显示累积使用量。

q:退出。


如果在shell脚本中使用iotop命令,需要加上-b选项,即静态显示的模式,还需要加另外几个选项,具体用法如下:

# iotop -obn2
Total DISK READ :       0.00 B/s | Total DISK WRITE :       0.00 B/s
Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s
   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
Total DISK READ :       0.00 B/s | Total DISK WRITE :       0.00 B/s
Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s
   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
 45078 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.09 % [kworker/0:0]

说明:-o跟上面那个快捷键o是一个意思,它的作用是只显示有IO的进程。-n2表示需要统计2次,因为第一次不会显示任何进程。


本案例参考脚本

#!/bin/bash
##监控磁盘IO使用率,并找出哪个进程造成磁盘使用率很高
##该脚本需要写一个常驻循环
##作者:
##日期:

#判断机器上是否安装iostat命令
if ! which iostat &>/dev/null
then
    yum install -y sysstat
    #如果你的机器为ubuntu,请使用这个命令:apt-get install -y sysstat
fi

#判断机器上是否安装iotop命令
if ! which iotop &>/dev/null
then
    yum install -y iotop
    #如果你的机器为ubuntu,请使用这个命令:apt-get install -y iotop
fi

#定义记录日志的目录
logdir=/tmp/iolog
[ -d $logdir ] || mkdir $logdir

#定义日志名字
dt=`date +%F`

#定义获取io的函数(取5次平均值)
get_io()
{    
    iostat -dx 1 5 > $logdir/iostat.log
    sum=0

    #取最后一列的%util值循环遍历然后相加
    for ut in  `grep "^$1" $logdir/iostat.log|awk '{print $NF}'|cut -d. -f1`
    do
        sum=$[$sum+$ut]
    done
    echo $[$sum/5]
}

#这里的true表示条件为真
while true
do
    #获取所有设备,对所有设备名遍历
    for d in `iostat -dx|egrep -v '^$|Device:|CPU\)'|awk '{print $1}'`
    do
        io=`get_io $d`
        #如果io使用率大于等于80
        if [ $io -ge 80 ]
        then
            #向日志里记录时间、iostat和iotop信息
            date >> $logdir/$dt   
            cat $logdir/iostat.log >>$logdir/$dt
            iotop -obn2 >>$logdir/$dt
            echo "####################" >>$logdir/$dt
        fi
    #休眠10秒,继续以上步骤
    done
    sleep 10
done