今回は、パフォーマンスチューニングについて考えてみます。
ハードウェアは進化しているのに、なぜパフォーマンスチューニングを続けるのか
インフラエンジニアは10年前に比べて、格段にパフォーマンスチューニングのスキルを要求されるようになっています。CPUが高速になりストレージも高速になり、メモリの単価も安くなっているにもかかわらず、です。これはひとえに、ネットの、というかWebのサービスの傾向によるものに他なりません。
いまやWebはネットのかなりの部分のトラフィックを締めており、そのWebがどんどん双方向化しています。ここでいう双方向化というのは、大多数のユーザも情報を発信するようになっている、ということとほぼ同義です。
インターネットは双方向(通信)というのはかなり以前から言われていますが、そうは言ってもたとえば2000年のころWebは双方向だったか? というとそんなことはないと思います。Webはごくごく一部の企業や人が情報を発信し、それ以外の大多数が情報を受信するという特性の媒体でした。
それが、2chの登場で匿名ながらも情報発信が盛んになり、blogの登場でわずかながらも自分のコンテンツを持つ人が増えてきて、SNSの登場でネットにいわゆる「自分の場所」を持つことが普通のことになってきて、ECやニュースサイトがソーシャル機能を持つようになり、TwitterやFacebookのようなソーシャルメディアが活発に使われるようになって、急速に発信される情報が増えてきました。
特に、「こまかい情報」がたくさん発信されるようになってきています。当然ながら、同じだけの量のデータの場合、細かくたくさん発信されるほうがシステムへの負荷は上がります。そして、こういったロングテールコンテンツは、WebのPVあたりの収益性を見ると相対的に低いケースが多い気がします。そうなると、限られたサーバリソースによっていかに負荷に耐えるか、ということが重要になってきます。
いずれにしても、これからのインフラエンジニアは(特にWebの)パフォーマンスチューニングのスキルによって、その評価がかなり変わってきます。良い面としては、以前は縁の下の力持ち的な位置付けだったインフラエンジニアが一躍脚光を浴びるポジションになってきたことです。やはり脚光を浴びるポジションであるほうが目指す人は多くなりますし、やりがいも増えるので、これは良いことだと思います。
パフォーマンスチューニングの基本
では筆者が思うところのパフォーマンスチューニングについて考えてみたいと思います。もちろん時代はどんどん進化しているので、古い部分もあるかもしれません。とはいえ時代を越えて共通な点もあると思います。
まずパフォーマンスチューニングの最大のポイントのひとつは、関係する要素を全て把握することです。例としてCPU、ディスク、メモリ、ネットワーク、外部サービスあたりがあげられます。他にもたとえばプロセスという切り口もありますが、これは実質CPUとメモリです。Webのパフォーマンスチューニングに限らず、パフォーマンスを要求されるケースに共通することは、ボトルネックを解消することです。
ボトルネックは、ある時点においてパフォーマンスの限界になっているポイントです。もちろんこれは刻々と変化します。ただ、いずれにしてもある時点でどこがボトルネックになっているかを知るには、関係する要素の全てを知っている必要があります。全てです。
1ヵ所でも知らないところがあってそこがボトルネックの場合、それ以外をどんなに手を入れてもあまり効果はありません(ゼロではないのですが)。理想をいえば、全ての要素がほぼ同じくらいボトルネックになっているのが一番性能を限界まで引き出している状態なのですが、まあそれは現実には不可能なため、だいたいはその時点での突出したボトルネックを解消して、それによって発生する別のボトルネックを解消、ということを繰返していきます。
ところで、たとえば上記であげた要素は、おおむね2つのグループに大別できます。それは「演算」と「I/O」です。インフラエンジニアがチューニングの対象にするボトルネックはたいがいがI/Oです。演算というのはすなわちCPUで、それはプログラムによるものです(「プログラムはCPU」ではないので注意が必要です)。
たとえば無駄にループが深いといった、いわゆるホットスポットといわれる部分に手を入れることで、パフォーマンスが格段に向上することも良くあることですが、ここでは「プログラムの修正は除いた」パフォーマンスチューニングについて考えてみます。
もちろん、「いまのボトルネックはCPUのようだからこれはプログラムの修正を検討したほうが良いかもしれない」と思うことは立派なインフラエンジニアの仕事ですし、プログラムが読み書きできて実際の原因まで突きとめられればそれは最高です。また、最近のプログラムは加速度的に複雑になってきていて、プログラム的には手を入れきってあってもCPUが足りないことも多々あります。ただ、CPUに関していえばインフラエンジニアができることはあまりありません。できることは、CPU自体を増やす、CPUを速いものに交換する、1台あたりのCPUの数をバランスさせる、といったところです。
UNIXもしくはLinuxでの話になりますが(WindowsやMacOSもそうですが)、プログラムというのはユーザランド部分とシステムコール部分に分かれます。システムコールというのはプログラムだけでどうにもできないので処理をカーネルに委託する部分で、これはその大部分がI/Oの操作になります。つまり、上記の「演算(CPU)」と「I/O」という大別は、言い換えれば「ユーザランド」と「システムコール」という大別に置き換えることができます。
さて、たとえばシステム負荷を知る方法としてuptimeというコマンドがあります。uptimeによって1, 5, 15分の負荷の平均が表示されますが、ここで表示される値は何を意味しているのでしょうか? これはとりもなおさず、プロセスのrunnable queueの値です。つまり「プログラム的には実行できる状態になっているのに待機している」プロセスの数です。もちろん、ユーザランドプログラムすなわち演算能力待ちで待機しているプロセスもありますが、多くのケースではこれはI/Oによって引き起こされています。
I/Oにはディスクやネットワーク、メモリなどがありますが、メモリがボトルネックになることはまずありません(メモリが小さくてスラッシングが、というのはメモリI/O自体に話ではないですね)。つまりインフラエンジニアが取り組むパフォーマンスチューニングはディスクとネットワークが対象のことが多い、ということになります。さらにさらにいえば、ネットワークよりはディスクのほうがネックになるケースのほうが格段に多いです。ここにCPUも入れて考えてみると、ディスク、CPU、ネットワーク、メモリという順にボトルネックになりやすい気がします(もちろんケースバイケースです)。つまり、まず最初に習得するべきはディスクI/Oについてです。
ディスクI/Oを制する者はチューニングを制する
ディスクI/Oに限らず、チューニングするためには計測できなければなりません。ディスクI/Oを計測する方法はvmstatやsadcをはじめとして多々あるので、まずはそれらを把握して、定量的にディスクI/Oの状態を把握できるようになることが重要です。ディスクI/OにはReadとWriteがあります。ReadとWriteによってチューニングはもちろん変わってきます。ReadよりはWriteのほうが難易度は高いといえるでしょう。
さて、計測できたとしてどうチューニングすればいいのでしょうか。ディスクI/Oは特にハードウェアと密接に関連しているので、まずはハードウェア側に手を入れるということが考えられます。より速いディスクにするというのは効果的な解決策です。
よくあるのは、回転数の高いディスクにするということです。ディスクは回転数が高くなるとかなりパフォーマンスに効果が出ます。また搭載しているキャッシュを大きいものにするという方法もあります。またRAID化するというのも効果的な方法です。ストライピング(RAID 0)はWriteを、ミラーリング(RAID 1)はReadのパフォーマンスを向上させます。
また厳密にディスクI/Oではないのですが、バスを速いものにするという手もあります。SATAとSASではかなりパフォーマンスに差がでます。また、あまり頻繁にやる方法ではないですが、ディスクのアロケーションを調整するという手もあります。よく使うデータはディスクの外周に、あまり使わないデータはディスクの内周に配置するとパフォーマンスが向上します。とはいえアロケーションを調整するにはアロケーションを調整できるファイルシステムを使う必要があります。
また現段階で究極のディスクI/OのチューニングはSSDにすることだと言えるでしょう。現在はまだそれほどサーバにおけるSSD利用は一般的ではないですが、近い将来SSDはハイパフォーマンスサーバの常識になることでしょう。
なぜディスクはそれほどパフォーマンスチューニングの対象になることが多いのでしょうか?
それは、遅いくせに使用頻度が高いからに他なりません。データは読み書きできないと話にならないうえ、永続的に保存できる必要があるので、使用頻度が高いのはこれはどうにもなりません。それほどよく使われる部分なのにもかかわらず、物理的に動作するデバイスなのです。ディスクのプラッタ(円盤)は、モーターの力で回転しているのです。そしてディスクヘッドも物理的に移動するという有様です。
CPUやネットワーク、メモリは電子が駆け巡るだけでいいのに、ディスクは未だに物理的な動作なのですから、これは遅いのはあたり前です。SSD登場以前は「スピンドル(いわゆるディスク)の数を増やせ」はチューニングの一番効果が出やすい方法のひとつでした。また、話は逸れますが、良く故障するのもディスクですが、これも同じ理由です。物理的に動作しない部分よりは、物理的に動作する部分のほうが壊れやすいのは当然です。
「回転しているところから壊れる」は常識であると言えます。
ただ、繰り返しますが、こういった状況はSSDの登場によって革命的に変わりつつあります。SSDによってサーバからは回転する部分はクーラーファンくらいしかなくなるのです。まあ今回の趣旨はSSD礼賛ではないのでこれくらいにしておきます。
キャッシュも制する価値あり
ディスク自体のチューニングよりさらに強力なのがキャッシュの活用です。作業量対効果という意味でいえば、キャッシュは現在のパフォーマンスチューニングの相当な部分を占めていると言えるでしょう。ディスクは遅いけどメモリは速い、これを最大限活用するのがキャッシュです。たとえばディスク自体が搭載しているキャッシュもその1つです。またOSによりキャッシュ、ファイルシステムによるキャッシュ、プログラムによるキャッシュなど、キャッシュはあらゆるレイヤーに登場します。ディスクがSSDになってもキャッシュは引続き有効です(HDDより効果は相対的には減るでしょうが)。
また、キャッシュはディスクI/Oのパフォーマンスチューニングだけでなく、ネットワークのチューニングにも有効です。ネットワークは物理動作のない、電子が駆け巡ることで処理されている部分ですが、それでもメモリやCPUよりは相当遅いものです。それは、ひとえにメモリやCPUより「遠くに」データを運ぶためです。このため周波数(クロック)も上げられないし、伝送損失の発生による誤り訂正の頻度もかなり高いものになります。
厳密にいえば、ネットワークI/Oのデータ自体はネットワークレイヤとしてキャッシュするものではなく、外部サービスとしてキャッシュされるもののほうが多いでしょう。たとえばWebのプロキシキャッシュやDNSキャッシュなどです。とはいえ、スイッチのARPキャッシュなどもあることにはあります。
最近よく活用されるKVSやNoSQLといったものも、データをオンメモリに置いてキャッシュすることで効果を発揮します。SQLパース自体をなくすことで高速化している部分もあるため、いちがいにキャッシュ効果だけとは言えませんが(この部分は演算のチューニングであると言えます)。
ただ、キャッシュの利用というのはプログラムと切り離せないため、ディスクをRAID化するとか高速なものにするといったような、トランスペアレントなチューニングはあまりできません(ディスクのキャッシュを増やす、というケースもありますが)。ではどこでキャッシュを効かすかというと、それはOSの設定であったり、サーバの設定であったり、またはプログラムの改造であったりするわけです。
「その設定がなぜ高速なのか」を理解しよう
特にインフラエンジニアとしては、OSパラメータの設定(sysconfigやprocまわりなど)や、各種サーバプログラムの設定というのは、実質最もよく手をつける部分でしょう。ただ、ここで重要なのは、OSの設定やサーバの設定(ApacheやMySQLなど)をチューニングしているときに「なぜこの項目をこういう値にすると高速になるのか」を理解してやる必要があるということです。
本に書いてあるから、とか、どこかのblogに書いてあるから、とかだけでは駄目なのです。そして、各項目の調整がなぜ効果があるかを考えると、それはほぼI/Oの改善につながっているはずです。なぜMySQLの使用メモリを増やすと高速になるか? それはディスクI/OよりメモリI/Oのほうが高速だからです。なぜ増やしすぎると遅くなるのか?それはメモリがスワップアウトしてスラッシングを起こしメモリよりI/Oの遅いディスクが頻繁に使われるようになるからです。
パフォーマンスチューニングにゴールは無い
ちょっと言い過ぎかもしれませんが、パフォーマンスチューニングというのは最終的には必ずハードウェアに結びついているのです。つまりパフォーマンスチューニングをするには、その先で最終的にどうハードウェアに影響している/されているのかを考える必要があるということです。
またこの考えは、特に最近活用頻度が上がっている仮想環境においてさらに重要です。流行りのクラウドというワードを使っていもいかもしれません。仮想環境もいろいろな方式があるので一概には言えませんが、エミュレーション方式の場合、ハードに見える部分が実はソフトでそのソフトの先に本当のハードがあったりします。仮想環境でディスクI/Oのチューニングに苦労したことのある人はいると思います。OpenVZのような方式であればそこまで苦労はしませんが。
もちろんキャッシュは絶大な手法ではありますが、それ以外にもいろいろなチューニングがあります。たとえばMTUの調整はネットワークによってはかなり効果的です。MTUの調整はカーネルの負荷やネットワークカード、スイッチの負荷を下げられる可能性があるでしょう。
また外部サービスのチューニングというのも効果的な手法であることがあります。DNSのキャッシュサーバの変更が効果的なチューニングになるケースもあるでしょう。パフォーマンスチューニングの手法、対象はあまりにも多岐にわたるため、はじから挙げていくことは難しいですが、いずれにしても重要なのは、
- 要素を全て把握する
- 正しく計測してボトルネックをみつける
- ハードウェアまで見通してボトルネックを解消する
ということが重要です。
またこれに追加して、
ということも非常に重要です。すごく高度なチューニングテクニックをもっていても、たとえばSSDの登場のような技術の進化を知らなければ、良いチューニングができないかもしれません。
パフォーマンスチューニングに終わりはありません。それだけにやりがいが多いといえるでしょう。