うめすこんぶ

日々のプログラミングで残しておきたいメモ.何かの役に立てれば幸いです.

これからインフラを高速化したければ読んでおきたい「大規模サービス技術入門」

スポンサーリンク

はてなの伊藤直也さんが書いた本ということで有名な本。

一言で言うと、はてなぐらいの大規模なWebサービスに必要なパフォーマンスを担保するための技術の解説。 最初から最後まで、如何にパフォーマンスを上げるか、という論点が多く、速度上げるの好きな自分としてはかなり満足行く内容だった。

印象に残った点

はてなの規模は? (2009当時)

月間1500万UU 数十億アクセス/月

はてなブックマークだと、レコード件数が1000万から5000万 ブックマークのentryテーブルは1500万レコード、3GB

インデックスを付けないと、idで絞り込んでアクセスした結果が200秒まっても返ってこないレベル。 このぐらいの件数だとそんなに掛かるのか。。

規模が日本のWebの中ではトップレベルだと思う。データ量もGB単位が当然のようになる。 ホスト台数も仮想化ホストでいえば1000台をこえる。

時代背景

このころだとまだAWSの文字がでてこない。HTTPリクエストをApacheのmod_rewrite使うなど。

DBのパフォーマンス改善(局所化を考慮する)

高速化のためアクセスパターンの振り分けは大事だ。

キャッシュに乗ったデータになるべくアクセスするように、アクセスするテーブルごとにDBサーバの担当を振り分ける。 そうするとあるDBサーバに関しては類似したアクセスが来るようになるので、キャッシュヒットしやすい。

また、テーブル内でも局所化を考慮してカラムを分けている。 例えば、bookmarkテーブルは必須のbookmarkやuid(ユーザーID)などのカラムとis_privateなどあまり使わないカラムを別テーブルに分けている。テーブルをコンパクトにすることで、レコードがメモリに乗りやすくなる。

DBのインデックスについて

MysqlのインデックスはB木が使われている。このB木のノードをちょうどLinuxページサイズである4KBほどになるように調整する。 すると、各ノードはディスクに1回アクセスするだけで得られる。ディスクにアクセスする回数が短いほどI/O処理は高速化する。 二分木と比べると、1度のI/Oで一気に複数の値を読み出せるB木は、ディスクと相性がいい。

Mysqlのexplainをよくつかって、ちゃんとインデックスが効いているか、処理は重くないかチェックする。

サーバー単位でのHTTP振り分け(やっぱりキャッシュ大事!)

通常のサービス用APPサーバとボットやフィード用サーバを分けている。ボットやフィードの場合、アクセスが局所的でなく、 全体を舐めるような形になる。キャッシュに乗りにくいアクセスパターンだ。もしボットなどのアクセスを通常サービス用のサーバに一緒くたにすると、 キャッシュが効きにくくなる。

そこで、ボット用のアクセスをボットを担当するサーバに振り分けて、サービス用サーバのキャッシュを汚さないようにする。 特にはてなブックマークの場合、内部リンクがかなり多いので、ボットの巡回するページ数が多く、ボットアクセスが多くなる。

また、通常サーバ、ボット用サーバ意外に画像APIサーバも用意している。

サーバーの分散処理

DBサーバーはマスタ+スレーブ構成にする。スレーブは参照系のクエリを担当し、マスタは更新系を担当する。 はてなは参照系が大部分なので、参照系のスレーブを複数台構成してアクセスを分散させることで、性能がスケールできる。

更新系のクエリの場合、マスタで処理してからマスタの更新をスレーブに反映させる。スレーブはその名の通り、マスタに追従する役目を持っているわけだ。

大体のWebアプリケーションは90%が参照系クエリなので、更新系のサーバーが1台でも問題ない。

ただし、あまりに更新が多い場合は、別手段としてKVSにするのもあり。例えば、mixiの足跡など、ページビューのたびに更新が発生するものは更新クエリが多い。

はてなではTokyo Tyrantを使っている。

KVSって何がオトクなのか。これは自分の意見だが、RDBと比べるとイメージとしては機能が単純な分高速。RDBはロックやロールバック用のwriteaheadのログ書き込み、トランザクション処理など高機能で色々やってくれる分処理に時間がかかる。これらの処理をなくしてショートカットした更新処理を行うのがKVSだと考える。

また、テーブル分割する実装も面白い。テーブルを分割してサイズを小さくすることで、サーバー内でもディスクのアクセス分散がしやすくなるし、異なるサーバーで分散させることもできる。

分散によるデメリット

デメリットについてもしっかり説明があった。

テーブルを分割して複数台のサーバーにそれぞれのテーブルを置くパターンも有る。例えば2台のサーバーにテーブルを置くとする。一見効率的に見えるが、この場合、必要なサーバー台数は2台ではなく4台になる。なぜなら、各サーバーごとに冗長化が必要だからだ。

そのため、安易にテーブルを分散しようとすると、余計なコストがかかるので、コストをしっかり考える。また、メモリ増設で対応できるなら、そちらを優先する。

まとめ

パフォーマンスを上げるために実際に行っている実践的なテクニックばかりで、非常にためになった。サービス規模が大きくなって来るとこの知見がきっと活かされるんじゃないかと期待。

[Web開発者のための]大規模サービス技術入門 ―データ構造、メモリ、OS、DB、サーバ/インフラ (WEB+DB PRESS plusシリーズ)

[Web開発者のための]大規模サービス技術入門 ―データ構造、メモリ、OS、DB、サーバ/インフラ (WEB+DB PRESS plusシリーズ)