今日は、全く新しいプロジェクト(Greenfieldプロジェクト)に適用できそうな色々な最適化の方法を比較したメリットについてみていきたい。すなわち、過去の判断からの圧力を受けたり、それまで最適化をほとんどしていないプロジェクトのことだ。
具体的に言うと、比較したい最適化法とは、MySQL自体を最適化するか、キャッシュするかの2つである。これらの最適化法は、完全に独立した方法だということを先に言っておくべきだろう。そのうちの片方だけをやるようにこだわるのは、両方とも開発者のリソースを使うことだからだ。
クエリの最適化
この最適化は一般的には、MySQLに送られてきているクエリを確認することと、それらに対してEXPLAIN
を実行することから始まる。調査によっては、インデックスを追加したり、スキーマに少々の変更を加えるのはよくあることだろう。
利点
- 最適化されたクエリは通常、アプリケーションにアクセスするあらゆるユーザに対して高速に応答する。インデックスはデータに対して対数探索でのアクセス(電話帳から調べるような分割統治法)を提供するものなので、ある程度のデータ量の増大にもパフォーマンスを維持する。インデックスが張られていないデータへのクエリをキャッシュで隠してしまうのは、場合によってはデータ量増大に対しては悪い結果になりえる。データが増えるにしたがって、キャッシュにヒットしないユーザはアプリケーションが使えないぐらいひどいパフォーマンス問題に当たることになる。
- MySQL側を最適化することで、キャッシュを無効にしたり、有効期限切れのデータをキャッシュから読み込んでしまうことを心配する必要がない。
- MySQL側を最適化すると、システムのスタックをシンプルに保てるし、開発環境でも同期したり作業するのが簡単になる。
欠点
- クエリによってはインデックスを張るだけでは改善せず、アプリケーションによって改善が難しいスキーマ変更をしなくてはならない。
- スキーマ変更の方法によっては非正規化することになる(データが重複する)。これはDBAにとっては一般的なテクニックではあるが、全てのデータがアプリケーションによって更新されるのを確実にするか、トリガによってそういった変更を保証する必要がある。
- 最適化の方法がMySQL特有のものになることがある。つまり、ベースにあるソフトウェアが複数のデータベース上で動く者の場合、インデックスを張るよりもさらに複雑な最適化をするのが難しくなってしまう。
キャッシュを追加する
このタイプの最適化では、アプリケーションのプロファイリングが必要で、思い処理をMySQLからmemcachedやRedisといった外部のキャッシュに逃がすことになる。
利点
- MySQLで最適化するのが難しい重いクエリがあるアプリケーションの場合、非常にうまくいく。例えば、大きな集約やGROUP BYを使うクエリだ。
- キャッシュは、システムのスループットを挙げるのにうまくはたらく。例として、一度に多数のユーザがアプリケーションにアクセスしてくることによるスローダウンなどに対して。
- キャッシュを他のアプリケーションの最上位に置くのがより簡単。例えば、あなたのアプリケーションが、MySQLにデータを保存する他のソフトウェアパッケージのフロントエンド部分だったとすると、そのソフトウェアパッケージにデータベースに関する変更を加えるのは非常に難しい。
欠点
- データに対してたくさんのアクセスパターンがあると(異なるフォーマットで異なるたくさんのページに分かれているなど)、更新時にキャッシュを無効化するのが難しくなり、期限切れデータが返されることになる。これへの対策として、よりきめ細かなデータをキャッシュする必要がある。しかしこれは、キャッシュからのデータの取り出しのレイテンシがかかるようになるなど問題がある。
- 生成処理が重いオブジェクトをキャッシュすることは、キャッシュミスしたリクエストに対してパフォーマンスの急落を招く(クエリの最適化の#1を見よう)。よいパフォーマンスのあり方としては、ユーザ間のばらつきを狭めるべきであって、平均をみてはならない(キャッシュの場合そうなってしまいがち)。
- 甘い考えのキャッシュ実装では、cache stampedeのようなバグに悩まされる。ちょうど先週、複数のユーザが同時に同じキャッシュコンテンツを更新しようとしてデータベースサーバがダウンした人のお手伝いをしたところだ。これに対する正しい解決策は、キャッシュの再生成をシリアライズするロックのレベル付けを導入することだ。
結論
通常、MySQLの最適化をまず調べてみるように薦めていて、それが最もエレガントな解決策だと考えている。しかし長期的に見れば、ほとんどのアプリケーションはどちらのアプローチもある程度は実装するものだろう。