MySQL Thread Pool: 数千の同時接続からデータベースを救う「切り札」

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

なぜデータベースはピーク時に「ダウン」してしまうのか?

システムが順調に稼働している最中、突然のマーケティングキャンペーンなどでアクセスが急増することがあります。これこそが悪夢の始まりです。クエリが徐々に遅くなり、データ量はそれほど多くないにもかかわらずサーバーのCPU使用率が100%に跳ね上がり、最終的にデータベースが完全にフリーズしてしまいます。

多くの場合、その犯人はMySQLのデフォルトメカニズムであるOne-Thread-Per-Connectionにあります. MySQLは接続ごとに専用のスレッドを作成します。接続数が1000〜2000に達すると、CPUは実際のデータ処理よりも、スレッド間の頻繁な切り替え(コンテキストスイッチ)の調整に追われ、限界に達してしまいます。その結果、システムパフォーマンスは急降下します。

8コアサーバーを使用したSysbenchの実機テストでは、接続数を100から1500に増やすと、デフォルトのMySQLのスループットは最大45%低下しました。このパフォーマンス低下を食い止めるための解決策がThread Pool Pluginです。

Thread Poolの仕組みとは?

Thread Poolは、固定人数のスタッフがいる銀行の窓口のようなものだと想像してください。客が来るたびに新しいスタッフを雇う(スペースと管理コストが無駄になる)のではなく、Thread Poolは精鋭のワーカーレッド(worker threads)グループを維持します。接続は複数のグループ(thread groups)に分けられ、順番待ちのキューに入れられます。この手法により、CPUは処理に集中でき、リソースの競合を最小限に抑えることができます。

適切なエディションの選択

注意点として、Oracle公式のThread Poolは有償のMySQL Enterprise版限定の機能です。しかし、Percona ServerMariaDBを使用している場合、この機能は完全に無料で、非常に強力です。高い安定性が求められるシステムにおいて、これらは私の第一の選択肢です。

現在のステータスを確認するには、MySQLにログインして次のコマンドを実行してください。

SHOW VARIABLES LIKE 'thread_handling';

結果がone-thread-per-connectionであれば、システムはまだ従来の方法で動作しており、アップグレードが必要です。

本番環境向けの最適化設定

設定ファイル my.cnf(通常は /etc/mysql/my.cnf)を開き、[mysqld] セクションの下に以下のパラメータを追加します。これらは、約1000〜2000接続を処理するサーバー向けに私が調整したパラメータセットです。

[mysqld]
# Thread Poolを有効化
thread_handling=pool-of-threads

# サーバーのvCPUコア数に合わせるのが推奨
# 16コアサーバーなら16に設定
thread_pool_size=16

# スタックしたクエリを処理するために新しいスレッドを作成するまでの待機時間(ms)
thread_pool_stall_limit=50

# RAMを保護するための総スレッド数上限
thread_pool_max_threads=2000

# 実行中のトランザクションを優先し、リソースを素早く解放する
thread_pool_high_priority_mode=transactions

重要パラメータの解説:

  • thread_pool_size: 設定の「心臓部」です。欲張って高く設定しすぎないでください。リソースの無駄な調整を避けるため、CPUコア数と同じ値に保つのがベストです。
  • thread_pool_stall_limit: 私はこれを50msに下げています(デフォルトは通常500ms)。これにより、重いクエリで詰まりが発生した際のシステムの反応が速くなります。
  • thread_pool_high_priority_mode: このモードは非常に有用です。新しいタスクを受け取るよりも、進行中のトランザクションを完結させることを優先し、ロックを素早く解放するのに役立ちます。

ファイルを保存した後、変更を反映させるためにサービスを再起動するのを忘れないでください。

sudo systemctl restart mysql

Thread Poolの「健康状態」を監視する方法

設定して終わりではありません。以下のコマンドでランタイム指標を確認し、システムが本当に安定しているかを観察する必要があります。

SHOW GLOBAL STATUS LIKE 'thread_pool%';

特に Thread_pool_waits に注目してください。この数値が継続的に急増している場合は、クエリが遅すぎてスレッドが必要以上に長く占有されているサインです。この場合、インデックスの見直しSQL文の最適化が必要です。

ちょっとしたコツ:Thread Poolを使用している場合、SHOW PROCESSLIST コマンドで数千の接続が Sleep 状態に見えることがありますが、心配しないでください。これらは単にソケットを保持しているだけの接続であり、以前のようにCPUを消費することはありません。

管理者のための「ライフライン」設定

重いクエリによってThread Poolが完全に詰まってしまうと、管理者がログインして対処できなくなる可能性があります。管理専用のバックドア(extra port)を常に開けておくようにしましょう。

extra_port=3307
extra_max_connections=5

この3307ポートはThread Poolをバイパスするため、緊急時にシステムをハングアップさせているクエリを「kill」するための経路を常に確保できます。

実際の効果

Thread Poolは個々のクエリを高速化するものではありませんが、過負荷時にシステム全体が崩壊するのを防ぎます。私が管理しているMySQL 8.0サーバーでは、Thread Pool適用後、800接続が同時に発生した際に見られたCPU 100%へのスパイク現象が完全に解消されました。現在のCPU使用率は45〜55%で安定しており、システムの耐久性とレスポンスが明らかに向上しました。

大規模なユーザーを抱えるEコマースサイトやモバイルアプリを運用している場合、Thread Poolは必須の機能です。まずはStaging環境でテストを行い、自分の環境に最適な thread_pool_size を見つけ出してください!

Share: