② 遅延原因の調査
人間の体を診察して、病巣の特定ができたら、今度は、その部位がどのような病気であるかを特定することが必要になります。システムの場合でも同じで、ボトルネック個所が特定できたら、今度はボトルネックを作り出している原因を調査する必要があります。
実は、ボトルネックが発生する直接の原因は、大きく分類すると2つだけです。1つが、サーバのリソース消費によるもの。もう1つが何らかの流量制限です。
リソース消費によるボトルネック
リソースの種類
すべてのアプリケーションプログラムは、サーバのリソースを使うことで動作します。ここで言うリソースは、CPU・メモリ・ディスク・ネットワークの4つを指します。
これらのリソースを一切使わないアプリケーションはこの世にありません。しかし、サーバのリソースも有限であるため、どれか1つのリソースでも上限まで使い切ってしまえば、その時点で遅延発生の原因となります。CPUが100%まで使われてしまえば、メモリやディスクがいくら余裕があっても意味がありませんし、ディスクのbusy率が100%となり、キューが貯まりはじめれば、それはディスクがボトルネックになることを意味します。
このようにボトルネックは、そのサーバの最も弱いリソースによって決定されます。CPUを多く積んだ計算能力の高いサーバであっても、ディスクが先にボトルネックになれば、せっかくのCPUも宝の持ち腐れになるのです。鎖の全体としての強度が、それを構成する環(わ)の中で最も弱い一つによって決定されるように、システムのパフォーマンスもまた、最も弱い部分が決定するのです。
データベースの鉄則
システムのパフォーマンスは「最も弱い環」が決める
リソース消費の調べ方
OSのリソースがどのように使用されているかを調べるには、表2のようなコマンドを使用します。基本的には表に示す標準的なコマンドで足りるでしょうが、中にはHP-UXのglanceのように、より詳細な情報を調べるためにOSが独自に用意しているリソース取得コマンドもあります。
表2 OSリソースの取得ツール/コマンド
プラットフォーム | ツール/コマンド |
Windows系 | パフォーマンスモニタ |
UNIX/Linux系 | sar(メモリ含む全般)、cat /proc/meminfo(メモリ、Linux系OSのみ)、mpstat(CPU)、iostat(Disk I/O)、netstat(ネットワーク) |
これらのコマンドを利用することで、5秒や10秒など、パラメータで指定した間隔でのリソース使用状況をモニタして、ログへ出力できます[2]。ボトルネックとなっている処理が実行されている状態でモニタリングを行うことで、その処理がどのリソースを最も激しく消費しているかを突き止められるわけです。
また、リソースのモニタリングは「① 遅延個所の調査」と同じタイミングで行うことが一般的です。本稿では解説をわかりやすくするため2つのステップに分けていますが、実際には裏でモニタしながら処理を実測したほうが、二度手間にならず時間を節約できます。
データベースの鉄則
遅延個所の切り分けとリソースのモニタは一緒にやるのが効率的
流量制限によるボトルネック
リソースのモニタ結果、頭打ちになったリソースが発見できれば、それで遅延原因は特定できます。しかし時折、リソースに余裕があるのに処理が遅い、という不思議な現象が生じることがあります。病気だというので検査してみたけど、血液検査も尿検査も、数値は正常だった、という場合に相当します。
この場合、次に疑うべき病因は、流量制限です。すなわち、アプリケーションがリソース消費を抑えるため、リミッターをかけているケースです。代表的なケースとして、APサーバとDBサーバ間の接続を制御するコネクションプールを取り上げます。
流量制限としてのコネクションプール
3層構造[3]のWebシステムの開発に携わっている方であれば、コネクションプールは日常的に使用している技術でしょう。コネクションの確立にかかる負荷によるパフォーマンスの低下を避けるため、複数のコネクションを用意(プール)しておくものです。アプリケーションはこのプールの中から空きコネクションを使用し、処理が終わればプールに戻します。
つまり、コネクションプール本来の目的から見れば、これは流量制限ではなく、それ自体がパフォーマンスチューニングのための技術です。そして実際、プールに余裕がある場合には、特に問題はないのです(図3)。
コネクションプールがいっぱいになったときの動作
問題が起きるのは、アプリケーションから非常に多くの接続要求がやってきて、プールしていたコネクションがすべて使われてしまった場合です。
このとき、コネクションプールは設定によって主に次の3種類の動作をします。
- ① プールしているコネクション数の上限を増加させる(積極型)
- ② 溢れた接続要求を待ち行列(キュー)に並ばせる(我慢型)
- ③ 「プールが枯渇して接続できませんでした」というエラーを返す(厳格型)
これらは単独で使用されることもあれば、①+②とか①+③という併用もあります。たとえば、最初5個用意していたプールが枯渇したら、プールを徐々に増やすけど、それにも限度があって、100に達したら②や③の動作をする、という折衷案です。筆者の経験上では、①+②の折衷型が最もポピュラーです。
コネクションプールがボトルネックになってしまう場合
ここまでの説明でおわかりかもしれませんが、コネクションプールが流量制限のメカニズムとして動作してしまうのは、② 我慢型のケースです(図4)。これは厳格型も同様なのですが、こちらはエラーが返るためすぐに気づきます。我慢型は「黙って」遅くなるので、肝臓病のように発見しにくい病気です。この場合、プールされているコネクションの数は一定以上増えないため、DBサーバに過負荷がかかることはありません。しかし、キューに接続要求が貯まり続ければ、その分APサーバの滞留時間が延びていくのは自明です。これが、「APサー バもDBサーバもリソースに余裕があるのに処理が遅い」という怪現象を引き起こすしくみです。
遅延がコネクションプールによるものかを調べるには
遅延がコネクションプールの待ち行列によるものかどうかを調べるには、負荷をかけたときにキューに並んでいるコネクション数を監視することです。これは、DBMSではなくアプリケーションサーバ側のメトリクスで監視する必要があります[4]。
コネクションプールは、あくまで一例として挙げたにすぎません。このように、処理の並列度数に上限が存在して、かつ上限に達した場合はキューに並ぶしくみが実装されている個所では、原理的に同じ問題が発生し得る、ということを覚えておいてください。
データベースの鉄則
リソースに余裕があるのに処理が遅い場合は、キューを疑え