MySQLでNVMeの性能を最大限に引き出す:innodb_io_capacity設定からカーネルチューニングまで

MySQL tutorial - IT technology blog
MySQL tutorial - IT technology blog

狭い路地を走るフェラーリの物語

以前、秒間約50,000リクエストのトラフィックがある大手ECサイトのログシステムを担当していました。システムがI/Oボトルネックに陥った際、上司は数千ドルを投じてSamsung EnterpriseのNVMeアレイへのアップグレードを決定しました。NVMeのIOPSスペックは旧式のSATA SSDの数十倍もあるため、チーム全員が速度の飛躍的な向上を確信していました。

しかし、現実は厳しいものでした。データを移行した後も、iowaitの数値は真っ赤なままで、アプリケーションのレスポンスも遅いままでした。結局のところ、MySQLのデフォルト設定は、フェラーリを時速20km制限の狭い路地で運転しているようなものだったのです。性能を「解放」する方法を知らなければ、どんなに高価なハードウェアもただの飾りに過ぎません。

なぜデフォルトのMySQL設定はNVMeへの投資を無駄にするのか?

理由は単純です。InnoDBのデフォルトパラメータは、HDD(ハードディスク)が主流だった時代に書かれたものだからです。

  • 低すぎるIOPS制限: innodb_io_capacity のデフォルト値はわずか200です。それに対し、現在のミドルレンジのNVMeドライブであれば、簡単に300,000〜500,000 IOPSに達します。
  • 時代遅れのフラッシュメカニズム: InnoDBがデータをディスクに書き出す(Flush)方法が慎重すぎるため、ディスクがアイドル状態であるにもかかわらず、CPUがI/O待ちを強いられることになります。
  • カーネルのキュー: Linuxのデフォルト設定では、磁気ヘッドの動きを最適化するためにI/Oリクエストを並べ替えようとしますが、これはNVMeメモリチップにとっては全く不要な処理です。

ステップ1:InnoDBをフル稼働させる

my.cnf ファイルを開きましょう。ここがハードウェアの力を解き放つ場所です。

innodb_io_capacity の設定

このパラメータは、ストレージシステムのI/O処理能力を定義します。NVMeを使用している場合は、思い切ってこの数値を上げてください。脆弱な「200」という数字はもう卒業しましょう。

[mysqld]
# 単一のNVMeドライブの場合(例:Samsung 980 Pro/PM9A1)
innodb_io_capacity = 5000
innodb_io_capacity_max = 10000

# NVMeのRAID 0構成やエンタープライズ向けモデルの場合
# innodb_io_capacity = 20000
# innodb_io_capacity_max = 40000

innodb_flush_neighbors の無効化

HDDでは、隣接するデータブロックをまとめて書き込むことでヘッドの移動距離を減らす効果がありました。しかしNVMeでは、この処理は無駄にCPUリソースを消費するだけです。すぐに無効化しましょう。

innodb_flush_neighbors = 0

innodb_flush_method = O_DIRECT の使用

デフォルトでは、データはInnoDBバッファプールとOSのページキャッシュの両方に二重にキャッシュされます。O_DIRECT を使用すると、MySQLはディスクに直接書き込むようになり、RAMを解放し、書き込み遅延を最小限に抑えることができます。

innodb_flush_method = O_DIRECT

ステップ2:データの「通り道」を広げるためのLinuxカーネルチューニング

OSはMySQLとハードウェアの架け橋です。橋が狭ければ、どんなに強力な車でも速く走ることはできません。

I/Oスケジューラの変更

cfqdeadline といったスケジューラは、回転するディスクのために設計されました。NVMeの場合は none に変更して、カーネルの中間層による並べ替えをスキップすることで、レイテンシを大幅に削減できます。

# 確認(nvme0n1を実際のドライブ名に置き換えてください)
cat /sys/block/nvme0n1/queue/scheduler

# 最高速度を出すためにnoneに変更
echo none > /sys/block/nvme0n1/queue/scheduler

nr_requests のキューサイズを増やす

NVMeは並列処理に非常に優れています。OSが一度に多くのリクエストをキューに投入できるように許可しましょう。

echo 1024 > /sys/block/nvme0n1/queue/nr_requests

noatime マウントの設定

SELECT を実行するたびに、Linuxはデフォルトでファイルのアクセス時刻を記録します。データベースにおいて、これは極めて無駄な動作です。/etc/fstabnoatime を追加して、これらの余計な書き込み操作を排除しましょう。

/dev/nvme0n1p1  /var/lib/mysql  ext4  defaults,noatime  0 2

よくある失敗:innodb_log_file_size の設定忘れ

I/Oを極限まで最適化したにもかかわらず、5分ごとに書き込み速度がガクンと落ちるケースに遭遇したことがあります。調査の結果、犯人はわずか128MBに設定されていた innodb_log_file_size でした。

ログファイルがいっぱいになると、MySQLはチェックポイントを実行するためにすべての動作を停止します。NVMeを使用している場合は、ログファイルを少なくとも1GB〜2GBに設定することをお勧めします。これにより、チェックポイントの間隔が長くなり、高負荷下でもパフォーマンスが安定します。

innodb_log_file_size = 2G
innodb_log_files_in_group = 2

最適化はゴールではなくプロセスである

すべての数値をいきなり本番サーバーにコピー&ペーストしないでください。以下の4つのステップで進めましょう:

  1. ベンチマーク: 設定前に fio を使用してディスクの実際のIOPSを測定する。
  2. カーネルの調整: none スケジューラと noatime を優先的に適用する。
  3. MySQLの設定: io_capacityO_DIRECT に焦点を当てる。
  4. モニタリング: iostat -x 1 を実行する。もし %util カラムが60%以下で、アプリケーションが依然として遅い場合は、まだ innodb_io_capacity を上げる余地があります。

デフォルト設定はパフォーマンスの敵です。NVMeドライブに投じたコストを1円も無駄にしないよう、大胆に設定を変更してみましょう。アプリケーションからのレスポンス結果が、あなたの正しさを証明してくれるはずです!

Share: