計算機有三大件:CPU、內存、磁盤,這三者有一個“拖後腿”的,那就是磁盤。在生產環境,作爲數據庫角色的服務器磁盤建議拿至少4塊硬盤做RAID10,這樣既保證數據讀寫速度也保證數據的安全。如果使用普通的磁盤,即使CPU再強悍,最終的服務器性能也不會太好。
在我的職業生涯中,遇到過多次因爲磁盤I/O效率低而導致MySQL查詢非常慢的問題。對於一般的小網站來說,MySQL的查詢隊列(用show processlist查看)不會超過100個,甚至不會超過10個,這是因爲MySQL查詢速度非常快。如果查詢隊列數量突然變大,可能是因爲網站訪問量變大也可能是因爲磁盤讀寫速度變慢。
本案例背景是這樣的,一臺阿里雲的機器,收到告警磁盤IO達到100%,但是登錄機器後查看並沒有什麼異常,也就是說磁盤飆到100%只是短暫的一會兒。既然出現了100%的情況,那說明肯定是有某個進程有問題。由於這個問題並不是一直出現,所有排查起來有點困難。於是,想到寫一個監測腳本,來實時查看磁盤IO使用情況,當發現異常時,則通過一些查看服務器狀態的指令來記錄具體的指標,從而分析出是什麼造成的磁盤IO使用率100%。(系列學習:https://blog.51cto.com/cloumn/detail/22?from_distribution=VQcJXAFSXQExUwIAVAcGAw)
技巧一:使用iostat查看磁盤IO
如果你係統中沒有iostat命令,需要安裝sysstat包,CentOS安裝方法是:
# yum install -y sysstat
iostat命令如果不加任何選項,執行結果如下:
# iostat
Linux 3.10.0-862.el7.x86_64 (web30) 2018年11月06日 _x86_64_ (4 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
1.50 0.00 2.10 0.07 26.12 70.21
Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn
vda 15.95 1.95 108.97 545936 30453675
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
Linux 3.10.0-862.el7.x86_64 (web30) 2018年11月06日 _x86_64_ (4 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
1.50 0.00 2.10 0.07 26.12 70.22
Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn
vda 15.96 1.96 109.10 546984 30511811
avg-cpu: %user %nice %system %iowait %steal %idle
0.79 0.00 1.58 0.53 26.58 70.53
Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn
vda 376.00 0.00 2324.00 0 2324
avg-cpu: %user %nice %system %iowait %steal %idle
0.78 0.00 1.04 0.00 9.14 89.03
Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn
vda 0.00 0.00 0.00 0 0
第一個1表示每隔1秒打印一次,3表示一共打印3次。 iostat命令還有一個非常使用的選項-x,它可以顯示更多的信息,也是我最常用的一個選項,如下:
# iostat -d -x 1 2
Linux 3.10.0-862.el7.x86_64 (web30) 2018年11月06日 _x86_64_ (4 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
vda 0.00 0.06 0.04 15.91 1.95 109.11 13.92 0.34 21.07 4.49 21.12 1.10 1.75
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
vda 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包安裝得來的,在CentOS下安裝iotop的方法是:
# yum install -y iotop
安裝完成後直接輸入iotop回車,結果顯示跟top類似,它是動態實時查看各個進程的磁盤讀寫情況,效果如下:
# iotop
Total DISK READ : 0.00 B/s | Total DISK WRITE : 60.42 K/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
1419 be/4 nginx 0.00 B/s 3.55 K/s 0.00 % 0.00 % nginx: worker process
9634 be/4 www 0.00 B/s 49.76 K/s 0.00 % 0.00 % php-fpm: pool www
9646 be/4 www 0.00 B/s 7.11 K/s 0.00 % 0.00 % php-fpm: pool www
512 be/4 polkitd 0.00 B/s 0.00 B/s 0.00 % 0.00 % polkitd --no-debug
1 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % systemd --switched-root --system --deserialize 22
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]
8 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [rcu_bh]
9 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [rcu_sched]
對於各列的輸出,很容易理解,我們主要看第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 : 59.67 K/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
1416 be/4 nginx 0.00 B/s 3.51 K/s 0.00 % 0.00 % nginx: worker process
1417 be/4 nginx 0.00 B/s 3.51 K/s 0.00 % 0.00 % nginx: worker process
1418 be/4 nginx 0.00 B/s 3.51 K/s 0.00 % 0.00 % nginx: worker process
1419 be/4 nginx 0.00 B/s 3.51 K/s 0.00 % 0.00 % nginx: worker process
9638 be/4 www 0.00 B/s 3.51 K/s 0.00 % 0.00 % php-fpm: pool www
說明:-o跟上面那個快捷鍵o一個意思,它的作用是隻顯示有IO的進程。-n2表示需要統計2次,因爲第一次不會顯示任何進程。
本案例參考腳本
#!/bin/bash
##監控磁盤IO使用率,並找出哪個進程造成磁盤使用率很高
##該腳本需要寫一個常駐循環
##作者:阿銘
##日期:2018-11-06
#判斷機器上是否安裝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
當然,這個腳本還並不完美,因爲一旦發生磁盤IO使用率很高的情況,則會持續一段時間,這樣就會頻繁地記錄日誌。其實,根據以前案例二中我們學過的告警收斂的思路,可以把該腳本適當修改。希望你可以動手來寫一寫,這樣才能練習你的邏輯思維能力。 另外,你也可以把該腳本改爲發告警郵件的形式。
更多的知識點請瀏覽: https://blog.51cto.com/cloumn/detail/22?from_distribution=VQcJXAFSXQExUwIAVAcGAw