cpu iowait高排查的case


在之前的常見的Java問題排查方法一文中,沒有寫cpu iowait時的排查方法,主要的原因是自己之前也沒碰到過什么cpu iowait高的case,很不幸的是在最近一周連續碰到了兩起cpu iowait的case,通過這兩起case讓自己學習到了很多系統層面的知識,也許這些知識對於熟悉系統的人來說沒什么,不過對於寫Java的同學我覺得還是值得分享下(由於Java基本不用於存儲類型的場景,所以通常來說碰到iowait高的機會會比其他幾類問題更低很多)。

當出現iowait高時,最重要的是要先找出到底哪個進程在消耗io,以最快的速度解決問題,但linux默認的一些工具例如像top、iostat等都只能看到io的消耗狀況,但對應不到是哪個進程在消耗,比較好用的用來定位的工具是iotop,不過有些環境要裝上可能不太容易,裝上了后直接執行iotop,就可以看到到底是哪個進程消耗了比較多的io,對應解決問題而言,通常在找到進程后殺掉基本就算解決了(還有一種方法是通過打開syslog以及blk_dump來看一段時間內消耗io的進程,但在我的兩個case里試過效果不太理想)。

但通常而言,上面的方法只能算勉強解決了問題,但還是沒有定位到程序里哪個地方有問題,甚至有可能重啟仍然iowait很高,於是需要借助其他工具來進一步排查,所幸系統層面是有這樣的工具,主要可通過blktrace/debugfs來定位到到底是讀或寫哪個(或哪些)文件造成了iowait高(這個方法主要學習自阿里集團內核組的伯瑜的一篇blog)。

在裝上了blktrace后,先mount -t debugfs none /sys/kernel/debug下,然后可通過iostat查看到底是哪個設備在消耗io,例如假設看到是sda在消耗,那么即可執行blktrace /dev/sda,在執行時將會自動在執行的目錄下生成一些sda.blktrace.*的文件,當覺得采集的差不多后,即可ctrl+c停掉。

之后執行blkparse sda.blktrace.* > result.log,再生成了result.log后執行grep ‘A’ result.log | head -n 5看看在采集的這段過程中,消耗io比較多的地方在哪,例如在我碰到的case中執行后看到的為:
8,0 11 1 0.000071219 248366 A R 218990140 + 16 <- (8,9) 148994872
8,0 11 1 0.000071219 248366 A R 218990140 + 16 <- (8,9) 148994872
8,0 11 1 0.000071219 248366 A R 218990140 + 16 <- (8,9) 148994872
8,0 11 1 0.000071219 248366 A R 218990140 + 16 <- (8,9) 148994872
8,0 11 1 0.000071219 248366 A R 218990140 + 16 <- (8,9) 148994872
這里A后面的R到底的意思是讀(如果是寫則為WS),和之前iostat看到的是一樣的,io主要是大量的讀造成的。

通過上面的信息8,0和(8,9)可以看到主要的消耗是在sda9(這個通過iostat也可以看到),(8,9)后的148994872代表讀的扇區號。

再通過debugfs -R ‘stats’ /dev/sda9 | grep ‘Block size’可以找到sda9的block size,在我碰到的case中block size是4096,也是通常ext2/ext3文件系統默認的。

每個扇區的大小為512,148994872/(4096/512) = 18624359即可找到文件系統上對應的block號,之后通過debugfs -R ‘icheck 18624359′ /dev/sda9可找到對應的inode,在我碰到的case中,執行后的結果為:
Block Inode number
18624359 18284971
而debugfs還提供了通過inode number找到具體的文件的功能,於是就好辦了,繼續執行debugfs -R ‘ncheck 18284971′,執行后看到類似如下的信息:
Inode Pathname
18284971 [相應的文件名]
在找到了文件名后就好辦了,可結合之前的iotop或直接lsof找出對應的進程id,然后就可以看看從代碼上層面怎么避免對此文件的大量讀。

除了上面的這種case外,還有些情況的iowait其實是比較簡單的,例如讀寫了巨大的文件(通常在大量出現異常時可能會出現)…

在解決上周碰到的兩個cpu iowait高的case中,其中一個是如上面的業務代碼造成,但另一個則是和raid卡配置相關,因為從iostat來看,當時寫的量也不是很大,但iowait卻比較高,請系統的人幫忙看了后,告訴我是因為raid卡寫策略配置的問題,我之前對raid卡的這些配置完全不懂。

通過服務器上會帶有raid卡,而現在的raid卡基本是帶有cache的,為了保障cache里的數據的安全性,通常raid卡會帶有電池或電容,相對而言電容的故障率比較低,raid卡會提供寫策略的配置,寫策略通常是Write Back和Write Through兩種,Wirte Back是指寫到cache后即認為寫成功,Write Through是指寫到磁盤上才算成功,通常Raid卡的寫策略會分為正常時,以及電池或電容出問題時兩種來配置,而在碰到的case中是因為配置了當電池/電容出問題時采用Write Through,當時機器的Raid卡的電池出故障了,所以導致策略切換為了Write Through,能夠支撐的iops自然是大幅度下降了,而在我們的場景中,本地數據丟掉是無所謂的,所以Raid卡的策略可以配置為即使電池出故障了也仍然采用Write Back。

通常各種Raid卡都會提供工具來配置寫策略,例如HP卡的hpacucli,可通過cat /proc/scsi/scsi查看硬盤和Raid卡的信息(可以先用cat /proc/mdstat來查看raid信息),有助於確認raid卡的cache/cache容量/電池以及硬盤本身能支撐的iops等。

因為建議在碰到iowait高的場景時,可以先看看raid卡的寫策略,如果沒問題的話再通過iotop、blktrace、debugfs、lsof等來定位到具體的根源。

可見即使是對於Java的同學,在排查問題時對於各種系統工具、硬件層面的一些知識還是要有些了解,不一定要很深,但至少要知道,對於工具要會用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM