MySQL道普請便り

第247回Docker上のMySQLでjemallocを使用してみる

Webアプリケーション、特にRuby等のパフォーマンスチューニングをしているときに、glibc mallocではなくjemallocを使用するとレスポンスが良くなる、という体験をしたことがある方も多いのではないでしょうか。⁠ハンマーを持つ人には全てが釘に見える⁠ではないですが、適用できる先が無いか試してみたくなります。 実は、MySQLでもjemallocを組み込むことができます。

今回はDockerのMySQLでjemallocを使用する方法について紹介していきたいと思います。

検証環境

今回はDockerで建てたMySQLを使用します。ベースイメージとして8.4系の最新を使用するため、以下のコマンドでDockerを建てて、ローカルからアクセスができる状態になっているか確認しましょう。

% docker run --platform linux/x86_64 -p 127.0.0.1:3307:3306 -e MYSQL_ROOT_PASSWORD=my-secret-pw -e MYSQL_USER=kk2170 -e MYSQL_PASSWORD=my-secret-pw -d mysql:8.4.5

mysqlクライアントでアクセスが可能であることを確認しましょう。方法は以下の通りになります。

% mysql -uroot -pmy-secret-pw -h127.0.0.1 -P3307

執筆時点では、以下の通りMySQL 8.4.5を使用しております。

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.4.5     |
+-----------+
1 row in set (0.00 sec)

jemallocとは?

jemalloc(Jason Evans Malloc)は、パフォーマンスとスケーラビリティに優れたメモリアロケータです。

もともとはFreeBSD向けに開発されましたが、現在ではLinuxやmacOS、Windowsなど幅広い環境で利用可能です。特徴としては、スレッドごとにメモリ領域(arena)を持つため、ロックの競合が少なくマルチスレッド環境に強かったり、メモリの再利用効率が高く、長時間稼働しても断片化によるメモリ消費の悪化が起きにくい、メモリの断片化の抑制が期待できます。Ruby等のメモリアロケータとして使用すると、パフォーマンスが良くなる場合があります。

jemallocを使うと本当に速くなるのか?

では、MySQLにjemallocを組み込んだとして、実際にどのような効果が期待できるのでしょうか? 結論から言うと、⁠多スレッドで高負荷な環境」において特に効果が出やすいということになります。

たとえば、複数のWebアプリケーションが同時にMySQLにアクセスするような状況や、バッチ系処理が並列に走るようなケースでは、glibcの標準mallocではロック競合による待ち時間が発生しがちです。jemallocは、スレッドごとに独立したメモリアリーナを持つ仕組みを採用しており、このようなロック競合を大幅に減らすことができます。

また、長期間稼働したサーバではメモリの断片化もパフォーマンスの低下要因になりますが、jemallocは断片化耐性が高く、ヒープの安定性を保ちやすいという特徴もあります。

…と頭では考えてみますが、実際にパフォーマンスチューニングする際には⁠推測するな計測せよ⁠の原則に則り、それが本当に効果的だったのか数値で確認できる状態にして、計測をしつつ進めた方が良いです。

次では、Docker環境でjemallocを有効にしたMySQLコンテナを立ち上げる方法について解説していきます。

DockerのMySQLでjemallocを有効にする

MySQLにjemallocを組み込んだ場合の効果を確認するには、まず手軽に試せる環境が欲しいところです。そんなときに便利なのがDockerを使った検証環境です。ここでは、Docker上でjemallocを有効にしたMySQLコンテナを立ち上げる手順を紹介します。

Dockerfileを作成する

MySQL の公式イメージにはjemallocは含まれていないため、jemallocをLD_PRELOADで読み込ませます。

以下のようなDockerfileを用意します。

FROM mysql:8.4

USER root

# epelを登録してjemallocをインストールする
RUN microdnf install epel-release && microdnf install -y jemalloc
ENV LD_PRELOAD=/usr/lib64/libjemalloc.so.2
USER mysql

これを起動してみましょう。まずはイメージをビルドします。

docker build -t mysql-jemalloc .

その後、以下のように指定を行い起動しましょう。

docker run -d \
  --name mysql-jemalloc \
  -e MYSQL_ROOT_PASSWORD=example \
  -e MYSQL_DATABASE=testdb \
  -p 3308:3306 \
  mysql-jemalloc

起動したコンテナ内でjemallocが読み込まれているか確認してみましょう。確認にはlddコマンドが使用できます。lddはプログラムがどの共有ライブラリを使用しているかを調べるためのコマンドです。出力は長いのでgrepを使ってjemallocで引っ掛けてみましょう。

bash-5.1# ldd /usr/sbin/mysqld | grep jemalloc
        /usr/lib64/libjemalloc.so.2 (0x00007fbb4bd12000)

上記のようになっていればjemallocを使用しています。

また、Docker内部に入って簡単なクエリを実行して、動作しているかも確認してみましょう。

mysql>
mysql> select version();
+-----------+
| version() |
+-----------+
| 8.4.5     |
+-----------+
1 row in set (0.00 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.11 sec)

簡単なクエリであれば、動いていることがわかります。

まとめ

思ったよりも簡単に試してみることができたので、拍子抜けした方もいたかもしれません。jemallocはMySQLの標準構成では使用されていないものの、高負荷な環境やメモリ断片化の影響を受けやすいシステムにおいて、効果を発揮する可能性がある選択肢です。

とはいえ、jemallocを導入することは標準的ではないため、安易な適用はおすすめできません。本番環境で利用する前には、この記事のようにDocker などで十分に検証を行い、どのようなメリットとデメリットがあるのかを見極めることが重要だと思います。

ただし、MySQLにおいてもjemallocを使用する選択肢があることを知っておくだけでも、将来のパフォーマンスチューニングの引き出しが増えるはずです。

興味がある方は、まずは開発環境からぜひ試してみてください。

おすすめ記事

記事・ニュース一覧