ブロックデバイスのI/O状況の計測
それでは、ブロックデバイスのI/O状況の分析を始めましょう。まずはiostat
コマンドを使って、ブロックデバイスのI/O状況を計測します。実行コマンドとその出力結果例は次のとおりです。
iostat -d -mx 10 | awk '{print(strftime("%Y/%m/%d %H:%M:%S"),$0);fflush();}'
2015/10/26 13:15:43 Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await svctm %util 2015/10/26 13:15:43 sda 0.00 13.55 0.00 0.67 0.00 0.06 167.51 0.01 11.82 10.08 0.68 2015/10/26 13:15:43 dm-0 0.00 0.00 0.00 14.14 0.00 0.06 8.00 0.13 9.38 0.48 0.68 2015/10/26 13:15:43 dm-1 0.00 0.00 0.00 0.00 0.00 0.00 8.00 0.00 20.05 1.89 0.00
出力された各項目の意味は次表のとおりです。
項目 | 意味 |
---|---|
Device: | ブロックデバイス名 |
rrqm/s | マージされた(結果として1つのI/Oとなった)リードI/O数(/秒) |
wrqm/s | マージされた(結果として1つのI/Oとなった)ライトI/O数(/秒) |
r/s | 成功したリードI/O数(/秒) |
w/s | 成功したライトI/O数(/秒) |
rMB/s | リード量(MB/秒) |
wMB/s | ライト量(MB/秒) |
avgrq-sz | 1I/Oあたりの平均サイズ(セクタ数) |
avgqu-sz |
キューに存在した平均I/O数 ※キュー待ち+デバイス上で処理中だったI/O数 |
await |
1I/Oあたりの平均応答時間(ミリ秒) ※キュー待ち+デバイス上で処理中だった時間 |
svctm |
1I/Oあたりの平均サービス時間(ミリ秒) ※デバイス上で処理中だった時間 |
%util | サービス時間(デバイス上で処理中だった時間)の割合(%) |
※ iostatの初回データはサーバ起動時からの平均値となるため、通常は解析対象から除外します。
ブロックデバイスのI/O状況の分析
iostat
コマンドでブロックデバイスのI/O状況を計測できたので、いよいよその分析に入ります。分析は、まずI/O傾向を把握し、次にI/O負荷状況を確認するという流れで分析を行います。出力項目のうち、avgqu-sz、svctm、%utilは勘違いしやすい項目であるため注意が必要です。技術書籍やインターネットなどの情報では、avgqu-sz、svctm、%utilに着目して分析するよう解説されていることがありますが、I/O傾向を把握せずにそれらの項目のみで分析を行うと、判断を誤る可能性が高くなります。
I/O傾向を把握する(r/s、w/s、rMB/s、wMB/s、avgrq-sz)
1秒間あたりに処理されたI/O数のことをIOPS(I/O per second)といいます。このIOPS(r/s、w/s)を確認することで、リードとライトのどちらにI/Oが偏っているのかを見分けられます。また、前述したファイルシステムとブロックデバイスの対応関係を確認することで、どのファイルシステムでI/Oが多いのかが分かります。
ただしリードの場合、iostat
コマンドは汎用ブロック層を計測しているため、ファイルシステム層でキャッシュヒットした分は計測されません。言い換えれば、リード量(rMB/s)が多い状況は、キャッシュヒット率が低下している可能性を示しています。前回の記事を参考に、ディスクキャッシュが不足していないかを確認しましょう。
次に、平均I/Oサイズを示す「avgrq-sz」を確認します。この値が小さい(10セクタ程度の)場合、ランダムアクセスの傾向が強いです。逆に値が大きい(数100セクタ以上の)場合は、シーケンシャルアクセスの傾向が強いです。データベース処理のように、インデックスを用いて少量データの抽出を頻繁に行う場合にはランダムアクセスの傾向となり、巨大データのリード処理やライト処理ではシーケンシャルアクセスの傾向になりやすいです。
一般的に、ハードディスクにおけるランダムアクセスは磁気ヘッドの移動を伴う確率が高いため、物理的なI/O効率がよくありません。そのため、I/Oスケジューラ上で小さなI/Oを並べ替え・マージして、物理I/Oが効率的となるよう最適化が行われます。マージによって削減されたI/O数は、「rrqm/s」および「wrqm/s」で確認できます。
ストレージのスループットを評価する際は、これらのアクセス傾向を意識することが大事です。ランダムアクセスの場合は、サイズの小さいI/Oをいかにさばくかが重要なため、IOPS(r/s、w/s)が重視されます。一方、シーケンシャルアクセスの場合は、転送量(rMB/s、wMB/s)が重視されます。シーケンシャルアクセスではI/Oサイズが大きくなるためIOPSは減少します。
また、ストレージ製品のハードウェアスペックのスループット値と、iostat
コマンドによる実測値を照らし合わせることで、ハードウェアの性能限界に近いのか、まだまだ余力があるのかを判断することもできます。
なお、リード量およびライト量が多いユーザプロセスがどれかを確認するには、「pidstat -d
」コマンドが便利です。このコマンドは基本的にrootユーザで実行します。root以外のユーザでpidstat
コマンドを実行した場合、リード量およびライト量は当該ユーザが起動したプロセスのみ計測されます。ただし、pidstat
コマンドではIOPSではなくバイト単位の計測になることと、リード量はiostat
コマンドと同様にキャッシュヒットした分は計測されないことを覚えておきましょう。
I/O負荷状況を確認する(%util、await)
%utilが100に近ければ、I/O処理を行っている時間の割合が大半を占めていることを示すため、I/O負荷が高いと考えられます。また、アプリケーション側がI/O処理の応答で待たされているようであれば、%utilが20〜50程度でもI/O負荷の軽減対策を検討すべきでしょう。
ただし、%utilはCPU使用率のようなリソース限界を示す項目ではないため、ストレージ側が性能限界になっている(それ以上スループットが出ない)かは見分けがつきません。特に、RAID構成やSSDおよびフラッシュアレイなどの高速なストレージを採用している環境では勘違いしやすいので気をつけてください。
性能限界かどうかを判断するには、スループットが頭打ちになっているか、await(I/O応答時間)が増加しているかを合わせて確認しましょう。また、外部ストレージを利用しているのであれば、外部ストレージ側の性能情報も合わせて確認することが望ましいです。
ここで具体例を挙げて解説してみたいと思います。I/O処理を行うアプリケーションを1〜16多重で動作させたときの、iostat
コマンドによる測定結果を以下に示します。
多重度 Device: r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await svctm %util 1多重 sda 352.20 0.00 1.38 0.00 8.00 0.98 2.78 2.78 97.94 2多重 sda 610.70 0.00 2.39 0.00 8.00 1.98 3.24 1.64 99.92 4多重 sda 1161.10 0.00 4.54 0.00 8.00 3.97 3.42 0.86 100.00 8多重 sda 1615.90 0.00 6.31 0.00 8.00 7.95 4.92 0.62 100.00 10多重 sda 2003.00 0.00 7.82 0.00 8.00 9.94 4.96 0.50 100.00 16多重 sda 1876.00 0.00 7.33 0.00 8.00 15.94 8.50 0.53 100.00
ちなみに「sda」はOSからは1個のストレージに見えますが、実際はRAID10(SASハードディスク6個)のストレージです。
%utilが100なのに8MB/s未満しか性能が出ておらず、svctm(I/Oサービス時間)が1ms程度なのでストレージ側は遅延していないように見えることから、アプリケーションかOS上の何かがおかしい、と判断してしまうのは早計です。落ち着いて各項目を分析しましょう。アプリケーション多重度を増やして負荷をかけていくと、%utilが100になっているにもかかわらず、10多重まではr/sとrMB/sが増加し続けています。w/sがゼロであることと、avgrq-sz(平均I/Oサイズ)が8セクタと小さいことから、リード処理でランダムアクセス中心の負荷パターンといえます。ランダムアクセス時のI/O性能は、転送量ではなくIOPSが重視されることを思い出してください。avgqu-sz(キューに存在する平均I/O数)はアプリケーション多重度とほぼ同じ値を示しており、多重度どおりの負荷がかかっていることが分かります。また、16多重時には性能限界(過負荷状態)を迎えていると考えられます。なぜなら、10多重時と比べてr/sが低下し、await(I/O応答時間)が急増しているからです。
ところで、多重度増加とともにawait(I/O応答時間)は増加しているのに対し、svctm(I/Oサービス時間)は減少しています。この事象には矛盾ともいえる違和感があります。
実は、svctmは「%util × 10 ÷(r/s + w/s)」によって算出されているため、%utilの値が一定であれば、IOPSが増えるにつれてsvctmの値は減少します。iostat
コマンドのマニュアルにも、警告として「svctmの値を信用してはならない」と記載されており、将来的にsvctmは廃止されるそうです。
avgqu-sz(キューに存在した平均I/O数)が急増しているか
あくまで目安ですが、avgqu-szの値が高い場合には、ストレージ側が高負荷状態になりキュー待ちが発生している可能性があります。繰り返しになりますが、実際にストレージ側が高負荷状態かどうかは、スループットが頭打ちになっているか、await(I/O応答時間)が増加しているかを合わせて確認しましょう。なぜなら、avgqu-szの値は実際にキューに存在した(並んだ)I/O数を示しているのではなく、待ち行列理論にて算出された推論値だからです。
待ち行列理論のリトルの法則[Wikipedia]によると、「窓口に並んでいる人数 = 窓口に人が並ぶ・出て行く頻度(スループット)× 窓口の応対時間」となります。
avgqu-szは「デバイス上で処理中だったI/O数(in_flight/s)× デバイス上で処理中だった時間(svctm/1000)」によって算出されます。in_flightはカーネル内部のカウンタです。avgqu-szの値は、実質的には「IOPS(r/s + w/s)×(await/1000)」とほぼ同じ値を示します。
ところで、デバイス名「dm-X(論理ボリューム)」の計測結果は誤解しやすいため、慣れないうちは解析対象から除外したほうが無難です。以下に示す例のように、「dm-X」の IOPS(r/s、w/s)はマージされていない値であり、avgqu-szが大きい値を示すことになります。
2015/06/04 13:36:50 Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await svctm %util 2015/06/04 13:36:50 sda 0.00 5067.30 0.30 54.60 0.00 20.01 746.40 0.34 6.26 0.57 3.14 2015/06/04 13:36:50 dm-0 0.00 0.00 0.30 5121.90 0.00 20.01 8.00 36.61 7.15 0.01 3.14 2015/06/04 13:36:50 dm-1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
dm-0では、「avgqu-sz 36.61 ≒(read 0.30/s + write 5121.90/s)×(await 7.15ms/1000)」という大きい値になっていますが、write 5121.90/sは、実デバイスであるsdaでは、マージによって5067.30/s削減され、54.60/sになっています。したがって、ここは「avgqu-sz 0.34 ≒(read 0.30/s + write 54.60/s)×(await 6.26ms/1000)」という値で判断すべきです。
また、avgrq-sz(平均I/Oサイズ)については、dm-0では値が小さいためにランダムアクセスのように見えますが、sdaでは値が大きいため、実際はシーケンシャルアクセスということになります。このような見え方をふまえると、単純に確認できる項目は、転送量(rMB/s、wMB/s)のみということになります。
await(I/O応答時間)が遅延しているか
先の「I/O負荷状況を確認する」項で例を挙げて示したように、I/O量が少ない状況と多い状況におけるawaitの値を比較することで、遅延が発生しているか否かを見分けることができます。awaitが遅延している場合、その要因にはI/Oスケジューラやデバイスドライバ上での遅延、あるいはストレージの過負荷による遅延が考えられます。
I/O量が多いことが要因という場合もありますが、ストレージ側のキャッシュヒット率が低下しているといったこともありえます。また、外部ストレージを共有で使用している場合には、他サーバからのI/O負荷影響によって外部ストレージ側で遅延が発生した可能性も考えられます。このように、awaitの遅延にはさまざまな原因が考えられるため、OS上で計測できるリソース使用状況だけでは切り分けが難しいといえます。
単純にawaitとsvctmの値を比較すること(差が大きければOS側の遅延とみなすこと)は勘違いのもとです。外部ストレージ側の性能情報(コントローラのCPU使用率、キャッシュヒット率、ディスクビジー率など)を確認することが賢明です。
外部ストレージ側のキャッシュヒット率といえば、リード処理を思い浮かべる方が多いと思いますが、ライト処理も重要です。一例を挙げますと、大量にライト処理を行った際に外部ストレージ側のライトキャッシュが溢れてしまい、ライト性能が急激に劣化したという事例があります。また、ライトキャッシュを使わずにライトスルーモードとして外部ストレージを設定していたことで、ライト性能が全く出ないばかりか、ライト処理のI/O競合に引きずられてリード性能も劣化してしまったという事例もあります。キャッシュの状態には十分注意しましょう。
ちなみに、sysstatバージョン9.1.2以降のiostat
コマンドではr_awaitとw_awaitの項目が追加されており、リードとライトが混在した状況下でも、awaitの値を区別して確認できるようになっています。
(補足説明)CPU使用率内訳の%iowaitについて
前回、CPU使用率内訳の%iowaitが高い場合、ストレージに対するI/O負荷が高いことでI/O待ちが発生していると解説しました。ただし、%iowaitが低いからといって、ストレージに対するI/O負荷が低いとは限りません[5]。なぜなら、%iowaitは「I/O待ちによるCPUが使用されていない時間の割合」だからです。
つまり、CPU負荷が高い状況では、CPUが使用されている時間の割合が大きいため、I/Oが高負荷でも低負荷でも%iowaitは低い値を示します。したがって、%idleが高く、かつ%iowaitが低い場合、I/O負荷は低いということが想定されます。%idleと%iowaitがともに低い(CPU負荷が高い)場合、%iowaitの値からI/O負荷状況を見分けることはできませんので気をつけましょう。
注
[5]: CPUを複数搭載しているサーバでは、「CPU全体の平均としては%iowaitは低いが、特定のCPUだけは%iowaitが高い」というケースがあります。このケースではI/O待ちが生じており、ストレージに対するI/O負荷が高いと考えられます。