辩证看待 iostat
舊博文,搬到 csdn
原文:http://rebootcat.com/2018/01/16/using-iostat-dialectically/
前言
經常做系統分析會接觸到很多有用的工具,比如 iostat,它是用來分析磁盤性能、系統 I/O 的利器。
本文將重點介紹 iostat 命令的使用,并分析容易引起誤解的幾個指標。
iostat
iostat - Report Central Processing Unit (CPU) statistics and input/output statistics for devices and partitions.
上面是 man 手冊關于 iostat 命令的介紹,非常簡單明了。iostat 是我們經常用來分析 cpu 負載和磁盤 I/O 情況的工具。
iostat 基本使用
常用命令(個人習慣):
iostat -xk 2 10
參數的解釋可以查看 man 手冊:
OPTIONS-c Display the CPU utilization report.-d Display the device utilization report.-g group_name { device [...] | ALL }Display statistics for a group of devices. The iostat command reports statistics for each individual device in the list then a line of global statistics for the group displayed as group_name and made up of all thedevices in the list. The ALL keyword means that all the block devices defined by the system shall be included in the group.-h Make the Device Utilization Report easier to read by a human.-j { ID | LABEL | PATH | UUID | ... } [ device [...] | ALL ]Display persistent device names. Options ID, LABEL, etc. specify the type of the persistent name. These options are not limited, only prerequisite is that directory with required persistent names is present in/dev/disk. Optionally, multiple devices can be specified in the chosen persistent name type. Because persistent device names are usually long, option -h is enabled implicitly with this option.-k Display statistics in kilobytes per second.-m Display statistics in megabytes per second.-N Display the registered device mapper names for any device mapper devices. Useful for viewing LVM2 statistics.-p [ { device [,...] | ALL } ]The -p option displays statistics for block devices and all their partitions that are used by the system. If a device name is entered on the command line, then statistics for it and all its partitions are displayed.Last, the ALL keyword indicates that statistics have to be displayed for all the block devices and partitions defined by the system, including those that have never been used. If option -j is defined before thisoption, devices entered on the command line can be specified with the chosen persistent name type.-T This option must be used with option -g and indicates that only global statistics for the group are to be displayed, and not statistics for individual devices in the group.-t Print the time for each report displayed. The timestamp format may depend on the value of the S_TIME_FORMAT environment variable (see below).-V Print version number then exit.-x Display extended statistics.-y Omit first report with statistics since system boot, if displaying multiple records at given interval.-z Tell iostat to omit output for any devices for which there was no activity during the sample period.
簡單講,-x 參數能比較詳細的給出一些指標,2 代表間隔時間為 2s,統計輸出 10 次。
上面的命令可以看到如下的輸出:
avg-cpu: %user %nice %system %iowait %steal %idle0.40 0.00 0.49 0.42 0.00 98.69Device: 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 253.00 0.02 10.26 0.66 2081.56 405.05 0.65 62.78 6.01 62.92 4.55 4.68
sdb 0.00 0.00 0.00 0.00 0.00 0.00 8.19 0.00 0.23 0.23 0.00 0.23 0.00
sdc 0.00 0.00 0.00 0.00 0.00 0.00 8.19 0.00 0.32 0.32 0.00 0.32 0.00
sdd 0.00 0.00 0.00 0.00 0.00 0.00 8.19 0.00 0.34 0.34 0.00 0.34 0.00
sde 0.00 0.00 0.00 0.00 0.00 0.00 8.19 0.00 0.34 0.34 0.00 0.34 0.00
上面各個字段的解釋如下(同樣來自 man)
Device Utilization Reportrrqm/sThe number of read requests merged per second that were queued to the device.wrqm/sThe number of write requests merged per second that were queued to the device.r/sThe number (after merges) of read requests completed per second for the device.w/sThe number (after merges) of write requests completed per second for the device.rsec/s (rkB/s, rMB/s)The number of sectors (kilobytes, megabytes) read from the device per second.wsec/s (wkB/s, wMB/s)The number of sectors (kilobytes, megabytes) written to the device per second.avgrq-szThe average size (in sectors) of the requests that were issued to the device.avgqu-szThe average queue length of the requests that were issued to the device.awaitThe average time (in milliseconds) for I/O requests issued to the device to be served. This includes the time spent by the requests in queue and the time spent servicing them.r_awaitThe average time (in milliseconds) for read requests issued to the device to be served. This includes the time spent by the requests in queue and the time spent servicing them.w_awaitThe average time (in milliseconds) for write requests issued to the device to be served. This includes the time spent by the requests in queue and the time spent servicing them.svctmThe average service time (in milliseconds) for I/O requests that were issued to the device. Warning! Do not trust this field any more. This field will be removed in a future sysstat version.%utilPercentage of elapsed time during which I/O requests were issued to the device (bandwidth utilization for the device). Device saturation occurs when this value is close to 100%.
上面的英文應該還是挺容易明白的,其中重點需要關注的是下面幾個指標:
- avgrq-sz:每個 IO 的平均扇區數,即所有請求的平均大小,以扇區(512字節)為單位
- avgqu-sz:平均意義上的請求隊列長度
- await:平均每個 I/O 花費的時間,包括在隊列中等待時間以及磁盤控制器中真正處理的時間
- svctm:每個 I/O 的服務時間。但注意上面的解釋
Warning! Do not trust this field any more。iostat 中關于每個 I/O 的真實處理時間不可靠 - util:磁盤繁忙程度,單位為百分比
分析建議:
當系統性能下降時,我們往往需要著重關注上面列出來的 5 個參數,比如:
- I/O 請求隊列是否過長?
- I/O size 是否過大或過小?
- 是否造成了 I/O 等待過長?
- 每個 I/O 處理時間是否過大?
- 磁盤壓力是否過大?
綜合分析上述指標,可以得到一定的性能分析結論,但需要注意一些陷阱。
注意陷阱
我們看到上面 iostat 的輸出如下:
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 253.00 0.02 10.26 0.66 2081.56 405.05 0.65 62.78 6.01 62.92 4.55 4.68
svctm 為 4.55 ms,即每個 I/O 處理時間為 4.55 ms,這其實是有點偏慢了,但是 await 卻高達 62.78 ms,為何?
上面可以看到總的 I/O 數為『讀 I/O』+ 『寫 I/O』 = 0.02 + 10.26 ≈ 11 個,假設這 11 個 I/O 是同時發起,且磁盤是順序處理的情況,那么平均等待時間計算如下:
平均等待時間 = 單個 I/O 處理時間 * ( 1 + 2 + 3 + ...+ I/O 請求總數 - 1 ) / 請求總數 = 4.55 * ( 1 + 2 + 3 + ... + 10) / 11 = 22.75 ms
解釋如下:
可以把 iostat 想像成 超市付款處,有 11 個顧客排隊等待付款,只有一個收銀員在服務,每個顧客處理時間為 4.55 ms,第一個顧客不需要等待,第二個顧客需要等待第一個顧客的處理時間,第三個顧客需要等待前面兩位的處理時間…以此類推,所有等待時間為 單個 I/O 處理時間 * ( 1 + 2 + 3 + …+ I/O 請求總數 - 1 ).
計算得到的平均等待時間為 22.75 ms,再加上單個 I/O 處理時間 4.55 ms 得到 27.3 ms:
22.75 + 4.55 = 27.3 ms
27.3ms 可以表征 iostat 中的 await 指標,因為 await 包括了等待時間和實際處理時間。但 iostat 的 await 為 62.78 ms,為何會比 iostat 得到的 await 值小這么多?why?
27.3 ms < 62.78 ms
再次查看計算方法,步驟和原理都是正確的,但其中唯一不準確的變量就是單個 I/O 的處理時間 svctm!另外就是前提假定了磁盤是順序處理 I/O 的。
那么是不是 svctm 不準確呢?或者磁盤并不是順序處理 I/O 請求的呢?
丟棄 svctm
我們一直想要得到的指標是能夠衡量磁盤性能的指標,也就是單個 I/O 的 service time。但是 service time 和 iostat 無關,iostat 沒有任何一個參數能夠提供這方面的信息。人們往往對 iostat 抱有過多的期待!
Warning! Do not trust this field any more. This field will be removed in a future sysstat version.
man 手冊中給出了這么一段模凌兩可的警告,卻沒有說明原因。那么原因是什么呢?svctm 又是怎么得到的呢?
iostat 命令來自 sysstat 工具包,翻閱源碼可以在 rd_stats.c 找到 svctm 的計算方法,其實 svctm 的計算依賴于其他指標:
/***************************************************************************** Compute "extended" device statistics (service time, etc.).** IN:* @sdc Structure with current device statistics.* @sdp Structure with previous device statistics.* @itv Interval of time in 1/100th of a second.** OUT:* @xds Structure with extended statistics.****************************************************************************/void compute_ext_disk_stats(struct stats_disk *sdc, struct stats_disk *sdp,unsigned long long itv, struct ext_disk_stats *xds){double tput= ((double) (sdc->nr_ios - sdp->nr_ios)) * 100 / itv;xds->util = S_VALUE(sdp->tot_ticks, sdc->tot_ticks, itv);xds->svctm = tput ? xds->util / tput : 0.0;/** Kernel gives ticks already in milliseconds for all platforms* => no need for further scaling.*/xds->await = (sdc->nr_ios - sdp->nr_ios) ?((sdc->rd_ticks - sdp->rd_ticks) + (sdc->wr_ticks - sdp->wr_ticks)) /((double) (sdc->nr_ios - sdp->nr_ios)) : 0.0;xds->arqsz = (sdc->nr_ios - sdp->nr_ios) ?((sdc->rd_sect - sdp->rd_sect) + (sdc->wr_sect - sdp->wr_sect)) /((double) (sdc->nr_ios - sdp->nr_ios)) : 0.0;}
其中重點關注:
xds->svctm = tput ? xds->util / tput : 0.0;
學過 C 語言的都知道這是一個三元運算符:
A ? B : C
表示如果 A 為真,那么表達式值為 B,否則為 C
tput 可以理解為 IOPS,即當 IOPS 非零時,svctm 等于 util / tput;否則等于 0。
tput 相當于 IOPS,下文會作解釋。
上面說的 svctm 的計算依賴的值就是 util,那么 man 手冊給出的警告應該廢棄 svctm 的原因是不是因為 util 的計算不準確呢?
util 磁盤利用率
上面說到應該廢棄 svctm 指標,因為它并不能作為衡量磁盤性能的指標,svctm 的計算是不準確的。但從上面的計算公式可以看到,唯一的不確定的變量是 util 的值。
util 是用來衡量磁盤利用率的指標,那么 util 是怎么計算的呢?還是上面的 compute_ext_disk_stats 函數:
void compute_ext_disk_stats(struct stats_disk *sdc, struct stats_disk *sdp,unsigned long long itv, struct ext_disk_stats *xds){double tput= ((double) (sdc->nr_ios - sdp->nr_ios)) * 100 / itv;xds->util = S_VALUE(sdp->tot_ticks, sdc->tot_ticks, itv);...}
進一步閱讀源碼找到 S_VALUE 的定義:
#define S_VALUE(m,n,p) (((double) ((n) - (m))) / (p) * 100)
且上面的注釋可以看到:
* @sdc Structure with current device statistics.* @sdp Structure with previous device statistics.* @itv Interval of time in 1/100th of a second.
最終得到 util 的計算方法為:
util = ( current_tot_ticks - previous_tot_ticks ) / 采樣周期 * 100
那么 tot_ticks 是什么呢?這里需要關注 stats_disk 這個結構體,查閱源碼在 rd_stats.h 文件中:
/* rd_stats.h */
/* Structure for block devices statistics */
struct stats_disk {unsigned long long nr_ios;unsigned long rd_sect __attribute__ ((aligned (8)));unsigned long wr_sect __attribute__ ((aligned (8)));unsigned int rd_ticks __attribute__ ((aligned (8)));unsigned int wr_ticks;unsigned int tot_ticks;unsigned int rq_ticks;unsigned int major;unsigned int minor;
};
這里看不出具體每個字段是什么意義,源文件也沒有作注釋,接著看 rd_stats.c 文件是怎么對結構體賦值的,源文件 rd_stats.c 中:
/***************************************************************************** Read block devices statistics from /proc/diskstats.*
*/__nr_t read_diskstats_disk(struct stats_disk *st_disk, __nr_t nr_alloc,int read_part){...if ((fp = fopen(DISKSTATS, "r")) == NULL)return 0;while (fgets(line, sizeof(line), fp) != NULL) {if (sscanf(line, "%u %u %s %lu %*u %lu %u %lu %*u %lu"" %u %*u %u %u",&major, &minor, dev_name,&rd_ios, &rd_sec, &rd_ticks, &wr_ios, &wr_sec, &wr_ticks,&tot_ticks, &rq_ticks) == 11) { ... }
...
}
核心代碼如上,具體來講,iostat 的使用其實是依賴于 /proc/diskstats 文件,讀取 /proc/diskstats 值,然后做進一步的分析處理。這里額外介紹下 /proc/diskstats 文件:
[root@localhost ~]# cat /proc/diskstats1 0 ram0 0 0 0 0 0 0 0 0 0 0 01 1 ram1 0 0 0 0 0 0 0 0 0 0 01 2 ram2 0 0 0 0 0 0 0 0 0 0 01 3 ram3 0 0 0 0 0 0 0 0 0 0 01 4 ram4 0 0 0 0 0 0 0 0 0 0 01 5 ram5 0 0 0 0 0 0 0 0 0 0 01 6 ram6 0 0 0 0 0 0 0 0 0 0 01 7 ram7 0 0 0 0 0 0 0 0 0 0 01 8 ram8 0 0 0 0 0 0 0 0 0 0 08 0 sda 82044583 3148 10966722840 222442157 24658460 2499170 2700969385 105371088 0 57897509 3281962528 1 sda1 4144 0 339790 2859 93359 82770 4180584 671453 0 534023 6743118 2 sda2 487 0 4114 28 0 0 0 0 0 28 288 3 sda3 8450 0 206387 3489 598140 1719768 413807296 6739177 0 1204240 67425378 4 sda4 82031488 3148 10966172437 222435779 23966958 696632 2282981505 97960444 0 57538914 3210355358 16 sdb 6696805 672 1028622736 99268437 3479149 1095853 385460280 4357778 0 80933531 1036240008 32 sdc 6535697 706 1003357408 101660311 3409287 1048913 370227528 4329287 0 82570947 1059876038 48 sdd 6555170 652 1005848496 98046714 3392381 1044610 369149464 4407316 0 80348361 1024518998 64 sde 6532011 671 1002703024 134576408 3406505 1054721 372497720 5792380 0 103162428 140366630
每個字段的意義解釋如下:
The /proc/diskstats file displays the I/O statisticsof block devices. Each line contains the following 14fields:1 - major number2 - minor mumber3 - device name4 - reads completed successfully5 - reads merged6 - sectors read7 - time spent reading (ms)8 - writes completed9 - writes merged10 - sectors written11 - time spent writing (ms)12 - I/Os currently in progress13 - time spent doing I/Os (ms)14 - weighted time spent doing I/Os (ms)
這里英文的解釋可能沒有很明白很清楚,尤其是第 7 、11、13 個字段的解釋,我們再用中文解釋一下:
| 域 | Value | Quoted | 解釋 |
|---|---|---|---|
| F1 | 8 | major number | 此塊設備的主設備號 |
| F2 | 0 | minor mumber | 此塊設備的次設備號 |
| F3 | sda | device name | 此塊設備名字 |
| F4 | 8567 | reads completed successfully | 成功完成的讀請求次數 |
| F5 | 1560 | reads merged | 讀請求的次數 |
| F6 | 140762 | sectors read | 讀請求的扇區數總和 |
| F7 | 3460 | time spent reading (ms) | 讀請求花費的時間總和 |
| F8 | 0 | writes completed | 成功完成的寫請求次數 |
| F9 | 0 | writes merged | 寫請求合并的次數 |
| F10 | 0 | sectors written | 寫請求的扇區數總和 |
| F11 | 0 | time spent writing (ms) | 寫請求花費的時間總和 |
| F12 | 0 | I/Os currently in progress | 次塊設備隊列中的IO請求數 |
| F13 | 2090 | time spent doing I/Os (ms) | 塊設備隊列非空時間總和 |
| F14 | 3440 | weighted time spent doing I/Os (ms) | 塊設備隊列非空時間加權總和 |
這里需要特別對第 7、11、13 個字段做一點解釋,第 7 個字段表示所有讀請求的花費時間總和,這里把每個讀 I/O 請求都計算在內;同理是第 11 個字段;那么為什么還有第 13 個字段呢?第 13 個字段不關心有多少 I/O 在處理,它只關心設備是否在做 I/O 操作,所以真實情況是第 7 個字段加上第 11 個字段的值會比第 13 個字段的值更大一點。
回到 rd_stats.c 源碼中,stats_disk 結構體是如何賦值的呢?
...
while (fgets(line, sizeof(line), fp) != NULL)
...
sscanf(line, "%u %u %s %lu %*u %lu %u %lu %*u %lu"" %u %*u %u %u",&major, &minor, dev_name,&rd_ios, &rd_sec, &rd_ticks, &wr_ios, &wr_sec, &wr_ticks,&tot_ticks, &rq_ticks) == 11)...
使用 fgets 函數獲得 /proc/diskstats 文件中的一行數據,然后使用 sscanf 函數格式化字符串到結構體 stats_disk 的不同成員變量中。仔細看代碼,格式符號有 14 個,但接收字符串的變量只有 11 個,這里要注意的是 sscanf 的使用:
sscanf 中 * 表示讀入的數據將被舍棄。帶有*的格式指令不對應可變參數列表中的任何數據。
這么一來,我們要尋找的 tot_ticks 就是第 13 個字段,也就是表示:
13 - time spent doing I/Os (ms),即 花費在 I/O 上的時間
我們再回到 util 的計算:
util = ( current_tot_ticks - previous_tot_ticks ) / 采樣周期 * 100
util 的計算方法是: 統計一個周期內磁盤有多少自然時間(ms) 是用來做 I/O 的,得出百分比,代表磁盤利用率。
上文對于 svctm 的計算提到 tput 這個變量代表 IOPS,這里額外做一點解釋:
/*rd_stats.c 中 read_diskstats_disk 函數內 */
/* 讀 I/O + 寫 I/O 數量 */
st_disk_i->nr_ios = (unsigned long long) rd_ios + (unsigned long long) wr_ios;
...
/* rd_stats.c 中 compute_ext_disk_stats 函數內 */
/* 當前讀寫 I/O 數量 - 上一次采樣時的讀寫 I/O 數量 */
double tput = ((double) (sdc->nr_ios - sdp->nr_ios)) * 100 / itv;
...
經過對 /proc/diskstats 各個字段的分析,不難得出,stats_disk 結構體中的成員變量 nr_ios 代表讀寫 I/O 成功完成的數量,也就是 IOPS。
再回過來,那么 util 的計算是準確的嗎?tot_ticks 的計算是準確的嗎?
經過上面的分析,tot_ticks 其實表示的是 /proc/diskstats 文件中第 13 個字段,表示磁盤處理 I/O 操作的自然時間,不考慮并行性。那么由此得到的 util 就失去了最原本的意義。
舉個簡單的例子,假設磁盤處理單個 I/O 的能力為 0.01ms,依次有 200 個請求提交,需要 2s 處理完所有的請求,如果采樣周期為 1s,在 1s 的采樣周期里 util 就達到了 100%;但是如果這 200 個請求分批次的并發提交,比如每次并發提交 2 個請求,即每次同時過來 2 個請求,那么需要 1s 即可完成所有請求,采樣周期為 1s,util 也是 100%。
兩種場景下 util 均是 100%,那一種磁盤壓力更大?當然是第二種,但僅僅通過 util 并不能得出這個結論。
再回到 svctm 的計算:
double tput = ((double) (sdc->nr_ios - sdp->nr_ios)) * 100 / itv;
xds->util = S_VALUE(sdp->tot_ticks, sdc->tot_ticks, itv);
xds->svctm = tput ? xds->util / tput : 0.0;
轉換上述兩個式子可以得到:
svctm = ( current_tot_ticks - previous_tot_ticks ) / (current_ios - previous_ios ) = 采樣周期內設備進行 I/O 的自然時間 / 采樣周期內讀寫 I/O 次數
故通過此表達式計算得到的 svctm 其實并不能準確衡量單個 I/O 的處理能力。如果磁盤沒有并行處理的能力,那么采樣周期內讀寫 I/O 次數必然減少,相應的,svctm 的計算就會偏大。
那回到開頭提出的疑問,假定順序請求情況下得到的平均等待時間 27.3ms 小于 iostat 看到的 await 62.78ms:
27.3 ms < 62.78 ms
現在可以解釋了:27.3 ms 的計算其實使用了偏小的 svctm 值,故得到的平均等待時間較 62.78ms 小很多。
iostat 辯證看待
分析到這里,原理已經很明白了,util 并不能衡量磁盤的利用率,svctm 的值失去了意義。期望通過這兩個指標獲得一個磁盤性能的衡量恐怕不行了!
但平常的分析,我們可以參考 iostat 的輸出,再結合其他的一些工具,進行多方面多方位的性能分析,才能得到比較接近真理的結論!
延伸
上文分析了 iostat 容易引起誤解的幾個指標,在使用 iostat 時我們需要辯證的看待 iostat 的結果。
但我們往往更希望獲得一個能夠衡量磁盤性能的指標,iostat 可能幫不上太多忙了,這時可能需要借助其他的工具了,比如 blktrace 這個工具,這才是分析 I/O 的利器!
參考
- 深入理解iostat
- 容易被誤讀的IOSTAT
- 深入分析diskstats
- [Linux 運維 – 存儲] /proc/diskstats詳解
總結
以上是生活随笔為你收集整理的辩证看待 iostat的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信跳一跳高分辅助踩坑
- 下一篇: 一键安装python3环境