scale-out

分散を考慮したMySQLの運用 DBのスケールアウト戦略

前回は、大きなデータを効率良く扱う OSのキャッシュと分散についてまとめた。ここでは、分散を考慮したMySQLの運用といったDBのスケールアウト戦略について解説する。

1 インデックスを正しく運用する

分散を考慮したMySQL運用、3つのポイント

  1. OSのキャッシュを活かす
  2. インデックス(索引)を適切に設定する
  3. スケーリングを前提とした設計

 

OSのキャッシュを活かす

  • 全データサイズに気を配る:データ量<物理メモリを維持、メモリが足りない場合は増設など
  • スキーマ設計がデータサイズに与える影響を考慮する

※正規化:クエリが複雑になって速度が落ちるときがあるため、速度とデータサイズのトレードオフを考える

 

インデックス重要

MySQLのインデックスは基本B+木(ビープラスツリー)というデータ構造である。B+木はB木(ビーツリー)から派生したデータ構造で、外部記憶の探索時にシークを最小化するツリー構造である。また、探索の計算量がO(log n)で、線形探索O(n)で探すよりも速い。B木は木を構成する各ノードが複数個の子を持つことができる「多分木」である。B木はハードディスク上に構築するのに向いているデータ構造のため、DBでよく使われる。

  • 二分木とB木との比較:各ノードの大きさを適当なサイズに決められるのがB木のメリット。つまりディスクのシークの発生回数をノードをたどるときだけに最小化できる
  • MySQLでインデックスを作る:B+木によってツリーのデータ構造ができる

 

インデックスの効果

例えば4000万件のタグテーブルからの検索を考えた場合、インデックスなし(線形探索)ではO(n)=最大4000万回の探索が必要であるのに対し、インデックスあり(B木で二分探索)ではO(log n)=最大25.25回の探索で行うことができる。こうした計算量的に改善されるだけでなく、ディスクシーク回数的にも改善されるのである。

※インデックスの作用:MySQLの癖として、インデックスを付与しているカラムを対象にしたクエリでも、発行しているSQLによってはそれが使われたり使われなかったりする。基本的にインデックスが使われるのはwhere、order by、group by の条件に指定されたカラムである。また、インデックスとして作用するのは明示的に追加したインデックスやプライマリキー、UNIQUE制約である。そのため、複数の絡むに同時にインデックスを効かせたい場合は複合インデックスを使わなければならない

 

explainコマンド

SQLを投げる前にexplainコマンドを実行することで、MySQLがインデックスが効くかどうかを確認することができる。explainコマンドで速度に気をつけることが重要。

 

2 MySQLの分散

MySQLのレプリケーション機能

レプリケーション機能(replication)とは、マスタ(master)を決めてそのマスタを追いかけるサーバ(スレーブ、slave)を決めてやると、マスタに書き込んだ内容をスレーブがポーリング(polling)して同じ内容に自分自身を更新するという機能である。スレーブはマスタのレプリカ(replica、コピー)になり、同じ内容のサーバを複数用意することができる。そして、APサーバからはロードバランサを経由してスレーブに問い合わせをすることで、クエリを複数サーバに分散できる。

アプリケーションの実装では、selectなどの参照系のクエリはスレーブへ、更新クエリはマスタへ発行することが重要。はてなではO/Rマッパで制御している。

 

マスタ/スレーブの特徴

参照系クエリはスレーブで分散すればいいためスケールするが、マスタはスケールしないという特徴を持つ。前者はサーバを増やすだけでいいが、メモリにフィットさせることが重要である。後者は更新系クエリが増えると厳しいが、Webアプリケーションは90%以上が参照クエリなため書き込みは相対的に少ない。なお、マスタ負荷はテーブル分割や別実装などで工夫するのがよい。

 

3 MySQLのスケールアウトとパーティショニング

MySQLのスケールアウト戦略

データがメモリに載るサイズの場合メモリに載せ、乗らない場合はメモリ増設かパーティショニングをする必要になる。パーティショニング(テーブル分割)とは、テーブルAとテーブルBを別のサーバに置いて分散する方法である。局所性を活かして分散できるためキャッシュが効くという特徴を持つ。

 

パーティショニングを前提にした設計

はてなブックマークではentryとbookmarkテーブルは一緒に置いていてJOINもしているが、tagとentryテーブルはJOINしていないという方針で作成されている。これは基本的にJOINクエリは、対象となるテーブルを将来にわたってサーバにまたがる分割を行わないというのが保証できそうなときしか使わないからである。

 

JOINを排除

はてなではORマッパにDBIx::MoCoという自社開発のライブラリを使っているため、2つのテーブルにまたがるAPIを呼んだときにも中でJOINを使わず、where…in…で結合するクエリを勝手に生成してくれるようになっている。

 

パーティショニングのトレードオフ

パーティショニングのメリットは、負荷が下がる点と局所性が増してキャッシュ効果が高くなることである。反面デメリットは、運用が複雑になり故障率が上がることである。運用が複雑になるとその分コストがかかるため、現在は2GBで数千円で購入できるメモリを増やすことで対応する方が楽である。さらに、4台1セットでサーバが必要となる冗長化を考慮すると、パーティショニングはあくまで切り札として考えるほうがよい。

 

最後に

DBのスケールアウト戦略は、インデックスを正しく運用すること、MySQLの分散、そしてスケールアウトとパーティショニングを考慮することである。

次回は、アプリケーション開発の要所である大規模データ処理実践入門について解説する。

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


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>