MySQL 8.0 リファレンスマニュアル


5.6.3.3 スレッドプール操作

スレッドプールは、それぞれクライアント接続のセットを管理するいくつかのスレッドグループから構成されます。 接続が確立されると、スレッドプールはラウンドロビン方式でそれらをスレッドグループに割り当てます。

スレッドプールは、その操作の構成に使用できるシステム変数を公開します:

  • thread_pool_algorithm: スケジューリングに使用する並列性アルゴリズム。

  • thread_pool_high_priority_connection: セッションのステートメント実行のスケジュール方法。

  • thread_pool_max_active_query_threads: 許可するグループ当たりのアクティブスレッド数。

  • thread_pool_max_unused_threads: 許可するスリープ中のスレッド数。

  • thread_pool_prio_kickup_timer: スレッドプールが、実行を待機しているステートメントを低優先度キューから高優先度キューに移動するまでの時間。

  • thread_pool_size: スレッドプール内のスレッドグループの数。 これはスレッドプールのパフォーマンスを制御するもっとも重要なパラメータです。

  • thread_pool_stall_limit: 実行中のステートメントが停滞しているとみなされるまでの時間。

スレッドグループの数を構成するには、thread_pool_size システム変数を使用します。 グループのデフォルトの数は 16 です。 この変数の設定のガイドラインについては、セクション5.6.3.4「スレッドプールのチューニング」を参照してください。

グループあたりのスレッドの最大数は 4096 (または 1 つのスレッドが内部で使用される一部のシステムでは 4095) です。

スレッドプールは接続とスレッドを区別するため、接続と、それらの接続から受信したステートメントを実行するスレッド間に固定の関係はありません。 これは、特定のスレッドがその接続からすべてのステートメントを実行するように、あるスレッドを 1 つの接続に関連付けるデフォルトのスレッド処理モデルとは異なります。

デフォルトでは、スレッドプールは各グループで一度に最大 1 つのスレッドを実行しようとしますが、最高のパフォーマンスを得るために一時的に実行できるスレッドが増える場合があります:

  • 各スレッドグループには、グループに割り当てられた接続からの着信ステートメントを待機するリスナースレッドがあります。 ステートメントが到着すると、スレッドグループはその実行をただちに開始するか、あとで実行するためにキューに入れます。

    • 即時の実行は、ステートメントが受信した唯一のもので、キューに入れられていたり、現在実行していたりするステートメントがない場合に行われます。

    • キューイングは、ステートメントの実行をすぐに開始できない場合に行われます。

  • 即時実行が発生すると、リスナースレッドによって実行されます。 (つまり、グループ内に一時的に待機しているスレッドがなくなります。) ステートメントがすぐに終了すると、実行中のスレッドがステートメントの待機に戻ります。 そうでない場合、スレッドプールはステートメントを停滞中とみなし、別のスレッド (必要に応じて作成して) をリスナースレッドとして開始します。 スレッドグループが停滞中のステートメントによってブロックされないように、スレッドプールには、スレッドグループ状態を定期的にモニターするバックグラウンドスレッドがあります。

    待機中のスレッドを使用して、ただちに開始できるステートメントを実行することによって、ステートメントがすぐに終了した場合、追加のスレッドを作成する必要はありません。 これにより、同時スレッド数が少ない場合に、もっとも効率的な実行が可能になります。

    スレッドプールプラグインが開始すると、それはグループあたり 1 つのスレッド (リスナースレッド) に加えてバックグラウンドスレッドを作成します。 ステートメントを実行するための必要に応じて、追加のスレッドが作成されます。

  • thread_pool_stall_limit システム変数の値は、先述の項目のすぐに終了するの意味を決定します。 スレッドが停滞中とみなされるまでのデフォルトの時間は 60 ミリ秒ですが、6 秒まで設定できます。 このパラメータは、サーバーのワークロードに適切なバランスがとれるように構成できます。 待機の値が短いと、スレッドはよりすみやかに開始できます。 短い値はデッドロック状況を回避により適しています。 長い待機の値は、長時間実行するステートメントを含むワークロードで有用で、現在のステートメントの実行時に多数の新しいステートメントが開始しないようにします。

  • thread_pool_max_active_query_threads が 0 の場合、グループ当たりのアクティブスレッドの最大数を決定するために説明したように、デフォルトのアルゴリズムが適用されます。 デフォルトのアルゴリズムでは、中断されたスレッドが考慮され、よりアクティブなスレッドが一時的に許可される場合があります。 thread_pool_max_active_query_threads が 0 より大きい場合、グループ当たりのアクティブスレッド数に制限が設定されます。

  • スレッドプールは、同時の短時間実行ステートメントの数を制限することに焦点を合わせています。 実行中のステートメントが停滞時間に達する前に、ほかのステートメントの実行の開始を妨げます。 ステートメントが停滞時間を過ぎて実行している場合、それは続行が許可されますが、ほかのステートメントの開始は妨げられなくなります。 このように、スレッドプールは、各スレッドグループに、複数の長時間実行ステートメントがあっても、複数の短時間実行ステートメントがないように努めます。 必要な待機時間に対する制限がないため、長時間実行ステートメントによって、ほかのステートメントの実行が妨げられることは望ましくありません。 たとえば、レプリケーションソースサーバーでは、バイナリログイベントをレプリカに送信しているスレッドは事実上永久に実行されます。

  • ステートメントはディスク I/O 操作またはユーザーレベルロック (行ロックまたはテーブルロック) を検出するとブロックされます。 ブロックによって、スレッドグループは使用されなくなることがあるため、スレッドプールがこのグループで新しいスレッドをただちに開始して、別のステートメントを実行できるようにするため、スレッドプールへのコールバックがあります。 ブロックされたスレッドが返されると、スレッドプールはそれをすぐに再開することを許可します。

  • 優先度が高いキューと優先度が低いキューの 2 つのキューがあります。 トランザクションの最初のステートメントは優先度が低いキューに入ります。 トランザクションの後続のステートメントは、トランザクションが進行中 (そのステートメントが実行を開始している) 場合、優先度が高いキューに入れられ、そうでない場合は優先度が低いキューに入れられます。 キューの割り当ては、thread_pool_high_priority_connection システム変数を有効にすることによって影響を受けることがあります。これにより、セッションのすべてのキューに入れられているステートメントが優先度の高いキューに入れられます。

    非トランザクションストレージエンジンまたは autocommit が有効にされている場合のトランザクションエンジンのステートメントは、この場合に各ステートメントがトランザクションであるため、優先度の低いステートメントとして扱われます。 そのため、InnoDB テーブルと MyISAM テーブルに対するステートメントを組み合わせると、autocommit が有効にされていないかぎり、スレッドプールは MyISAM に対するステートメントより、InnoDB に対するステートメントを優先します。 autocommit を有効にすると、すべてのステートメントの優先度が低くなります。

  • スレッドグループが実行のためにキューに入れられているステートメントを選択する場合、まず優先度が高いキューを調べて、次に優先度が低いキューを調べます。 ステートメントが見つかった場合、そのキューからそれが削除され、実行が開始されます。

  • ステートメントが優先度の低いキューに長くとどまりすぎた場合、スレッドプールは優先度の高いキューに移動します。 thread_pool_prio_kickup_timer システム変数の値は、移動までの時間を制御します。 スレッドグループごとに、10ms 当たり最大 1 つのステートメント (100/秒) が優先度の低いキューから優先度の高いキューに移動されます。

  • スレッドプールは、CPU キャッシュの使用を大幅に効率化するために、もっともアクティブなスレッドを再利用します。 これは、パフォーマンスに大きな影響を与える小さな調整です。

  • スレッドがユーザー接続からステートメントを実行している間、パフォーマンススキーマインストゥルメンテーションは、ユーザー接続にスレッドアクティビティーを報告します。 それ以外の場合、パフォーマンススキーマはアクティビティーをスレッドプールに報告します。

これは、スレッドグループがステートメントを実行するために複数のスレッドを開始している状況の例です。

  • 1 つのスレッドがステートメントの実行を開始しますが、長時間実行しているため、停滞中とみなされます。 スレッドグループは、最初のスレッドがまだ実行中であっても、別のスレッドに別のステートメントの実行の開始を許可します。

  • 1 つのスレッドがステートメントの実行を開始し、その後ブロックされ、このことをスレッドプールにレポートします。 スレッドグループは、別のスレッドに別のステートメントの実行の開始を許可します。

  • 1 つのスレッドがステートメントの実行を開始し、ブロックされましたが、スレッドプールのコールバックによってインストゥルメントされたコードでブロックが発生していないため、ブロックされたことをレポートしません。 この場合、スレッドはスレッドグループにまだ実行中であるように見えます。 ステートメントが停滞中とみなされるほどブロックが長く継続した場合、グループは、別のスレッドに別のステートメントの実行の開始を許可します。

スレッドプールは、増加する接続全体に拡張できるように設計されています。 さらに、アクティブに実行しているステートメントの数を制限することから発生する可能性のあるデッドロックを回避するようにも設計されています。 スレッドプールにレポートしないスレッドは、ほかのステートメントの実行を妨げないため、スレッドプールのデッドロックを引き起こすことは重要です。 そのようなステートメントの例を次に示します。

  • 長時間実行ステートメント。 これらによって、すべてのリソースがほんの少数のステートメントで使用されることになり、ほかのすべてのステートメントのサーバーへのアクセスを妨げる可能性があります。

  • バイナリログを読み取り、それをレプリカに送信するバイナリログダンプスレッド。 これは、きわめて長い時間実行する長時間実行ステートメントの一種であり、ほかのステートメントの実行を妨げないはずです。

  • MySQL Server またはストレージエンジンによって、スレッドプールにレポートされていない、行ロック、テーブルロック、またはほかの何らかのブロックアクティビティーでブロックされたステートメント。

どの場合も、デッドロックを避けるため、スレッドグループが別のステートメントの実行の開始を許可できるように、ステートメントがすぐに完了しない場合、停滞中カテゴリに移動されます。 この設計により、スレッドが長時間実行するか、ブロックされた場合に、スレッドプールはスレッドを停滞中カテゴリに移動し、ステートメントの実行の残りの間、ほかのステートメントの実行を妨げません。

発生する可能性のあるスレッドの最大数は、max_connectionsthread_pool_size の合計です。 これは、すべての接続が実行モードにあり、グループあたりに追加のステートメントを待機する 1 つの追加スレッドが作成される状況で発生する可能性があります。 これは必ずしも頻繁に発生する状態ではありませんが、理論的には可能性があります。


関連キーワード:  ステートメント, 実行, プール, グループ, サーバー, キュー, 変数, 開始, 接続, 優先