SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

イベントレポート

「実現したいことを計算機の問題に置き換えることが『技術力』」、伊藤CTOが“はてな流”大規模データ処理の極意を語る

関西オープンソースカンファレンス2008セッションレポート


ページキャッシュの仕組みとI/O負荷の軽減

 OSには基本的に、ディスクから読み込んだデータをメモリにキャッシュする機構がある。はてなでメインに使用しているLinuxにも、この「ページキャッシュ」の仕組みがある。

 ページはページングの最小単位で、ディスクの内容をメモリに読み込むときに作成される。これは破棄されずに、カーネルバッファの中に残される。次にアクセスするときには、さっき読み取ったページがキャッシュにあるので、ディスクにはアクセスしない。

 さらにLinuxではキャッシュの制限がなく、メモリが空いていれば全部キャッシュに回される。実際にsar -rコマンドで確認すると、次のようにメモリの使用率が99%を超えていることがある。

※発表資料より
% sar -r 1 10000
Linux 2.6.11-co-0.6.4 (colinux) 05/28/07
19:50:32 kbmemfree kbmemused %memused kbbuffers kbcached kbswpfree kbswpused %swpused kbswpcad
19:50:33 5800 1005888 99.43 28244 694088 262132 4 0.00 0
19:50:34 5800 1005888 99.43 28244 694088 262132 4 0.00 0
19:50:35 5800 1005888 99.43 28244 694088 262132 4 0.00 0
19:50:36 5800 1005888 99.43 28244 694088 262132 4 0.00 0

 これは決してメモリ不足なわけではなく、搭載している1GBのメモリのうち700Mくらいがキャッシュに使われている(kbcached)ということだ。ここで新しいサーバープロセスを立ち上げると、古いキャッシュを追い出して、実際のアプリケーションにメモリが割り当てられる。

 結論として、単純にメモリを増やすことで、I/O負荷を軽減させることができるということになる。実例として、メモリが4GBのときのデータベースの負荷をsarコマンドの出力で見ると、次のように表示される。

メモリ4GBの場合(※発表資料より)
14:10:01 CPU %user %nice %system %iowait %idle
14:20:01 all 8.58 0.00 5.84 16.58 69.00
14:30:01 all 7.41 0.00 5.14 17.81 69.63
14:40:01 all 7.74 0.00 4.97 18.56 68.73
14:50:01 all 7.02 0.00 5.01 16.24 71.72

 iowaitが20%くらいあるのが見てとれる。20%というと、大したことがないように思えるが、CPU負荷に換算するとほぼ4倍なので、100%に近いI/O負荷が発生しており、実は大変な状況になっている。

 メモリを8GBに増設して同じサーバーでしばらく動かしてみると、次のようにiowaitがほとんどなくなる。

メモリ8GBの場合(※発表資料より)
14:10:01 CPU %user %nice %system %iowait %idle
14:10:01 all 18.16 0.00 11.56 0.80 69.49
14:20:01 all 12.48 0.00 9.47 0.88 77.17
14:30:01 all 14.20 0.00 10.17 0.91 74.72
14:40:01 all 13.25 0.00 9.74 0.75 76.25

 このとき面白いのは、CPU負荷が上がっていること。これはCPUが本来するべき仕事を、きちんと行っているためだ。I/O負荷がかからずに、CPUだけが動き続けているのが理想的な状態といえる。

 サーバーの負荷には「CPU負荷」と「I/O負荷」がある。CPU負荷をスケーリングするのは簡単だが、I/O負荷のスケーリングは難しい。サイトが遅いときに「サーバーを増やしたらいいんじゃないか?」と言われることが多いが、サーバーを増設して解決できるのはCPU負荷。大抵のWebサービスの問題は、I/O負荷をどう分散するかにかかっている。

 最近はメモリが8GB~16GBというサーバーがスタンダード(大抵の1Uサーバーにはメモリスロットが8つあり、最近は2GBのモジュールが1番安いので、16GBがコモディティになる)なので、それより小さいデータ規模のアプリケーションなら、すべてメモリにキャッシュできる。

キャッシュを前提としたデータベースの分割

 一方で、キャッシュしきれない規模になったらどうするか。16GBをコモディティとするなら、32GBのメモリが必要になってしまったとき、ここでようやく複数サーバーにスケールさせるという話になる。

 I/O負荷分散では、データベースを分割して、キャッシュできないところをなくす。下図のように、データベースの赤い部分がキャッシュできてないとする。このとき青い部分にアクセスするパターンAと、赤い部分に来るパターンBがあれば、赤い部分と青い部分を別々のサーバーに分離してやると、両方のデータが全部キャッシュに乗るので、キャッシュできない箇所がなくなるという訳だ。

I/O負荷分散の例(※発表資料より編集部で作成)
I/O負荷分散の例(※発表資料より編集部で作成)

 具体的には、テーブル単位で分割する方法がある。はてなブックマークの「エントリ」テーブルに大量のアクセスが来て、「ブックマーク」テーブルを引くと重いという場合には、それぞれを別のホストで管理するとサクサク動くようになるという。

 また、検索のインデックスを辞書順に分割する方法もある。国内のWebサービスでは「mixi」が採用している。はてなでも、「はてなダイアリー」でこの方法を利用しているという。

リクエストのアクセスパターンを考慮した分割

 ちょっと変わった例としては、リクエストのアクセスパターンを考慮して分割する方法がある。次の図は、はてなブックマークで実際に行っている分割の例だ。

リクエストパターンによる分割(※発表資料より編集部で作成)
リクエストパターンによる分割(※発表資料より編集部で作成)

 通常のユーザーからのアクセスは、基本的に人気エントリーや注目エントリーがよく閲覧されるので、新しいデータにアクセスが集中する。一方で、Google BotやYahoo! Slurpなどは、古いデータまで“絨毯爆撃”するが、応答速度はそれほど速くなくても良い。もう1つは、はてなブックマークに特徴的な、記事のブックマーク数をリアルタイムで「xxx users」と赤い画像で表示させるAPI。いろいろなサイトで利用されているので、1日で数億ものアクセスがあるが、このAPIは数の画像を返すだけで良い。このように、ボットとユーザー、そして画像APIでは、リクエストのパターンが違うので、はてなではアクセスさせるデータベースをそれぞれ分けているという。

透過的に作用することを考慮した運用上のノウハウ

 運用面で大規模データを扱う際に気をつけなければいけないのは、OSを起動した直後、すぐにサーバーを立ち上げないということだ。キャッシュが全く構築されていないので、ディスクにアクセスしに行ってしまいハングアップしてしまう。

 次のデータは、OS起動直後に数GBのファイルをリードしたときのもの。最初はメモリがほとんど使われていない(4%)が、18時40分からの10分間でいきなり96%になっている。

OS起動直後に数GBのファイルをreadした結果(※発表資料より)
18:20:01 kbmemfree kbmemused %memused kbbuffers kbcached kbswpfree kbswpused %swpused kbswpcad
18:30:01 3566992 157272 4.22 11224 50136 2048276 0 0.00 0
18:40:01 3546264 178000 4.78 12752 66548 2048276 0 0.00 0
18:50:01 112628 3611636 96.98 4312 3499144 2048232 44 0.00 44

 このように、ページキャッシュは透過的に作用するので、大きなファイルを一気にリードするだけでキャッシュが溜まってくれる。ノウハウとしては、OSが起動したら、データベースのファイルを全部catして「/dev/null」に投げておくと、メモリにキャッシュが構築される。

 性能評価を実地のマシンで行うときにも、キャッシュが最適化されるまで少し時間がかかる。最適化されてない状態でテストしても意味がない。負荷がだんだん落ち着いたところで測定するのがポイントだ。

次のページ
MySQLの運用

この記事は参考になりましたか?

イベントレポート連載記事一覧

もっと読む

この記事の著者

CodeZine編集部(コードジンヘンシュウブ)

CodeZineは、株式会社翔泳社が運営する開発者のための情報メディアです。日々の開発に取り組むエンジニアやテクノロジーを学びたい方に向けて、プログラミングやAI活用、開発ツール、エンジニアの学びとキャリアに関する記事をお届けしています。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/3315 2012/07/09 14:52

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー