MySQL データベースに大量のテーブルを置いたらパフォーマンスが落ちた話
仮面ライダーエグゼイドのあの仮面ライダー離れしたまなざしにはあと何週間で慣れるだろうか、とぼんやり考えている kagata です。
さて、今回は最近遭遇したデータベースの障害についてのお話です。
事例
こんな構成のデータベースサーバがありました。
- ストレージタイプ「汎用(SSD)」の Amazon RDS
- MySQL 5.6系
- ストレージエンジンは InnoDB
- File-Per-Table モードが有効
あるとき、このサーバのパフォーマンスが急に落ちるということがありました。システムに大きな変更は入れていないはずなのに、スロークエリが急に増えてしまいました。
サーバの状態を確認したところ、下のような警告を出力しているのが見つかりました。
DB Instance * has a large number of tables and has the parameter innodbfileper_table set to 1, which can increase database recovery time significantly.
File-Per-Table モードとテーブル数の問題
上のような現象が起こったのは、InnoDB の File-Per-Table モードが有効、かつテーブルが大量にあったためでした。
まず、File-Per-Table モードとは、InnoDB のテーブルスペースをテーブルごとに用意するモードです。このモードには、メンテナンスや拡張がしやすくなる利点があります。くわしくは下記記事を参照してください。
漢(オトコ)のコンピュータ道: InnoDBのファイルサイズ管理
一方、公式リファレンスには File-Per-Table モードの欠点もいくつか取り上げられています。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 14.5.2 InnoDB File-Per-Table モード
このうち、今回は下の件に触れてしまったようです。
mysqld では、テーブル当たり 1 つのオープンファイルの処理を維持する必要があるため、大量のテーブルがある場合、パフォーマンスに影響が出る可能性があります。
テーブルが増えるほど、データベースの動作にあたり大量のファイルにアクセスする必要が出てくるため、ストレージの I/O 性能がボトルネックになってしまうようです。
実際、このサーバ上のデータベースには約3,000ものテーブルが載っていました。インフラ担当いわく、現状の構成では1,000テーブルくらいが限度であろうとのことでした。
解決策
ではどうすればいいのか。解決策はいくつか考えられます。
テーブルを減らす
これがいちばん本質的な解決策です。使わないテーブルはアーカイブして退避させるなど。AWS なら、 S3 や Gracia などが退避先として考えられます。
スケールする
でも、本当に大量のテーブルすべてを常にデータベースに載せておかないといけない事情もあるかもしれません。そんなときには物理で殴る手もあります。
ひとつのサーバ上のひとつのデータベースにすべてのテーブルを突っ込むのでなく、データベースを分割してサーバをスケールアウトする手が考えられます。
また、ストレージの I/O 性能が問題なので、より性能の高いストレージに入れ替える方法もあります。Amazon RDS には「汎用(SSD)」のほか、より性能の高いストレージ「Provisioned IOPS」が用意されています。インフラ担当いわく、こちらなら10倍の10,000テーブルくらいまで賄えるのでは、とのことでした。
File-Per-Table モードをやめて共有テーブルスペースを使う
ストレージの I/O 性能がテーブル数を制限する…というのが File-Per-Table モードの特性なら、それをやめてしまうという手も考えられないではありません。
ただ、その場合は当然ながら File-Per-Table モードの恩恵が受けられなくなってしまいます。
SQL をチューニングする
データベースへの負荷がそもそも軽ければ、パフォーマンスが苦しくても問題になりにくい…かもしれません。SQL のチューニングはやっておくに越したことはありません。
まとめ
- テーブルが増えすぎるとデータベースのパフォーマンスを落とすことがあります。節度あるテーブル数を。
- どうしてもテーブルが減らせない場合はデータベースサーバをスケールアウトしたり、高性能のストレージに乗り換えたりする手があります。