InnoDBのすゝめ(仮)を読んで或いはAppendix(前半)
注意
この記事の内容は完全に一部のエンジニアむけ、それもかなりニッチで高度な話題を取り上げます。エンジニア以外の人が読んでも意味が分からないだろうし、理解に必要な労力に対し、得られるものは多くはありません。
はじめに
私の尊敬するエンジニアである瀬島さんが力作スライドを公開されました。
前述の資料はDBAである瀬島さんの目線で書かれており、また内容としても比較的MySQLへの理解がある人向けであるため、アプリケーションエンジニアである私の目線から特に重要な点について解説・考察を補足いたします。
そもそものはなし
よほど激しい更新や、よほど大きいデータ量を扱わない限り、MySQLはデフォルトの設定で、常識の範囲内のコストで動作します。特にAWSなどのパブリッククラウドではかなり柔軟にリソースを確保することができます。MySQLは怖くない。
ではこの資料は何の役に立つのかという話ですが
- よほど激しい更新やよほど大きいデータ量を扱う
- とても非効率的な使い方をしてしまった
- 予算に大きな制限がある
このようなエッジケースに直面した場合に助けになります。そのようなエッジケースにどのようなシナリオがあり得るかというと
- 計画よりもサービスの成長が異常に速い
- アプリケーションで使用しているORMのクエリの効率が悪い
- 最初にサービス規模を大きく見積もっていたが実際は厳しく、コストカットが求められる
例えばこんな感じです。
ゲームの DB とゲーム以外の DB は、 考え方を変えて良い
ここで理解すべきことは、目的に合わせて合理的選択をすることでかかるコストが抑えられるということです。合理的選択を行うためには目標の設定とともにMySQLへの理解が必要です。つまるところMySQLのことをよくわかっているとコストを下げられる!
sharding のメリット
ここでは暗黙的にinnodb_file_per_table
が有効であるという前提で話がされています。この設定値はMySQL5.6あたりでデフォルト有効になったので、最近のMySQLを使っている場合、勝手に有効になっているでしょう。この設定について詳しい情報が知りたい場合、
MySQL :: MySQL 5.6 リファレンスマニュアル :: 14.5.2 InnoDB File-Per-Table モード
を参考にしてください。
テーブルのデータサイズは大きくなりすぎると運用上いくつかのデメリットがあります(具体的な例は資料を参照)。これはテーブルを分割する(sharding)ことである程度回避できます。ただし、これはクエリの自由度を下げることにもつながるので、シャーディングのキーを工夫することでその影響を最低限にとどめるとよいでしょう。
資料にないデメリットとしては、テーブルの数が多くなるので結果的にファイルシステム上に配置されるibdファイルの数が増えます。どの程度shardingを行うかにもよりますが、innodb_open_filesの設定値を実態に合うようにしましょう。
どうでもいい話ですが、私はalter tableに最大で3日かかったことがあります。
MySQL の機能を活かして、 Transaction はほどほどに考える
ここはぐっと高度な話になります。頑張ってついていくか、読み飛ばすかしましょう。
多くのSQLドライバーでは更新系クエリの返り値から、更新されたレコード数を得ることができます。改めてselect count(*)~whereで取得するよりも低コストかつ確実に数を数えることができるので、活用しましょう。
初回だけinsertして以降updateの場合~というのは、insertしようとして失敗、その後updateという動作が非効率的になるからです。insertするべき確率よりもupdateするべき確率が圧倒的に高い場合、いきなりupdateからのほうが効率的です。
MySQL 以外の データストアやキャッシュと、 うまく組み合わせる
これは最初の段落の発展形です。MySQL(InnoDB)は非常に堅牢なデータストアですが、アプリケーションのすべての場面で同一の堅牢性が要求されるわけではありません。MySQLの特性を理解し、不得意なもの(超高頻度更新・超リアルタイムなど)はそれにあったデータストアを組み合わせることでコストが下げられます。
資料に挙げられていないものでいえば、アプリケーションそのもののメモリの中にキャッシュとして保持するのもよい方法です。また、いくつかの方法を組み合わせてキャッシュを多段にすることもままあります。
アプリケーションエンジニアとしてはどのデータがどこに、どの程度の寿命でキャッシュされているかを把握することが重要です。
また、メモリ内キャッシュ(memcachedやローカルメモリ)はリスタートでデータが吹き飛ぶ可能性があり、その際にDBの負荷が急激に上がる場合があります。アプリケーションのつくり次第では連鎖的な障害を発生させる場合もあるので、そこも考慮したうえでDBの性能を見積もりましょう。