MySQL の InnoDB をオンメモリで使ってみる

こんにちは。 ATOM事業部の田村です。
今回は、MySQL の高速化の手段として、InnoDB エンジンをオンメモリで使った場合のパフォーマンスについて調査してみました。

はじめに

MySQL には InnoDB エンジンだけではなく、他にもいくつものストレージエンジンがあります。 その中でも MEMORY エンジンは、その名の通りデータをメモリ上に持つため、処理が極めて高速という特徴があります。 ただし InnoDB とはいろいろ違う部分があり、InnoDB で使えていた SQL がそのまま通るわけではありません。 単に「高速化したい場合は InnoDB の代わりに MEMORY エンジンを使う」というわけには行かず、使い方・使い所をそれなりに考えてやる必要があります。 (詳細は Web 上に良質な記事が見つかるのでここでは省きます)

MEMORY エンジンが真価を発揮するのは、キャッシュや一時的な集計など、速度が必要でデータを永続化しなくてよいケースですが、しかし使い方にクセがあるのがデメリットです。 だったら InnoDB のストレージごとメモリに乗せたらどうなのか? 使い勝手は変わらないため、単純に高速化したい場合には便利なのでは? ということで試してみます。

InnoDB をオンメモリで動かす

今回は docker で tmpfs を利用します。 https://matsuand.github.io/docs.docker.jp.onthefly/storage/

docker-compose.yml で MySQL 8.0 の公式イメージを利用しますが、 volumes ではなく tmpfs 以下に MySQL のストレージとログを定義することで、これらのパスがメモリ上にマウントされた状態になります。

    tmpfs:
      - /var/lib/mysql
      - /var/log/mysql

テスト環境

今回は以下の3つの環境で同様の処理を行い、その速度を比較します。

  1. MEMORY
  2. InnoDB on メモリ (今回作った環境)
  3. InnoDB on SSD (通常の環境)

MySQL 8.0 公式イメージを利用します。 処理の内容は、CREATE TABLE からデータのインポート、インデックスの作成とそれを用いた SELECT と JOIN、単純な UPDATE と DROP TABLE といった、よく使いそうなものです。 ただし MEMORY エンジンはデータ型やレコード長の制限などがあり、あまり大きなデータを扱うことができないため、 テストデータのサイズは小さめにしています。 参考までに、テストデータは最大レコードサイズ 56KB 程度、全データ量 30 MB 程度です。

実行環境は自分の開発環境 (Mac 2020) なので、いろいろなノイズはあると思います。そんなに厳密な検証ではありません。 これはベンチマークではなく、あくまでストレージエンジン間の比較と、ストレージを SSD とメモリに乗せた場合の比較なので、はたして有意な差が出るのか、どのぐらい出るかといったぐらいの検証です。

比較

テストは最初に SSD 上にテストデータが用意されている状態からスタートし、処理が完了するまでの所要時間を計測しています。 これを各環境ごとにそれぞれ連続20回行い、最初の10回のデータは取らず、後の10回のデータを採用しました。

A : MEMORY B : InnoDB on メモリ C : InnoDB on SSD
min 338 ms 501 ms 1014 ms
avg 401 ms 609 ms 1428 ms
max 541 ms 799 ms 2736 ms

結果

MEMORY が一番早いのは当然の結果ですが、InnoDB on メモリとそこまで大きな開きはありませんでした。 MEMORY エンジンにはキャッシュが実装されていない(メモリ上のデータをメモリにキャッシュしても二度手間になるだけ)など、速度だけでなくメモリ効率も InnoDB より良くなっていると思われますが、今回はそこまで検証できませんでした。

MEMORY エンジンは使い所が限られますが、InnoDB on メモリは使い勝手はそのままでしかも高速ということで、選択肢の1つにはなりそうです。 まったく同じ SQL が通るので、永続性が必要なら on SSD で、そうでない部分は on メモリで、という使い分けができます。

SSD もかなり健闘していますが、さすがにメモリとの比較では差がついてしまいました。 何度かテストしたのですが、SSD は処理速度の変動幅が大きく、あまり外れたものは異常値として除外しようかとも思ったのですが、ちょっと無視できない頻度で発生していたため除外せず入れてあります。 これが実行環境の問題なのか、SSD の特性なのか、そこまでは検証できませんでした。

注意

MEMORY エンジンは、最大テーブルサイズが設定によって制限されています。 https://dev.mysql.com/doc/refman/8.0/ja/server-system-variables.html (max_heap_table_size 参照)

しかし InnoDB ではその制限はないため、ストレージをメモリに乗せた場合、docker プロセスのメモリが枯渇するまでデータを入れることができてしまいます。 メモリが枯渇するとエラーになるのですが、この時の挙動が一定していません。 実験してみたのですが、docker 上の Linux から no space left on device エラーが返ってくるケースと、エラーもなく黙って docker プロセスが落ちるケースがありました。 確実に上記のエラーが返ってくるという訳ではなく、MySQL 以外にも OS、docker や MySQL ドライバなど関連するもの全ての挙動に影響されうる部分なので、これ以外の可能性も考慮した方がいいと思います。

おわりに

InnoDB でもストレージをメモリに乗せることで、大幅に高速化することができました。 そして SSD も思ったより早く、これが HDD だったら一体どうなることやら・・・ と時の流れを体感することしきりです。 いい時代になったものです。