...

Webサーバーのスレッドプール最適化:Apache 対 NGINX および LiteSpeed の比較

この記事では、 スレッドプールウェブサーバー Apache、NGINX、LiteSpeed の設定 並列性、レイテンシ、メモリ要件を制御します。負荷がかかった場合に重要な設定と、セルフチューニングで十分な設定について、1 秒あたりのリクエスト数の明確な違いとともに説明します。.

中心点

  • 建築プロセス/スレッド(Apache)対イベント(NGINX/LiteSpeed)
  • セルフチューニング: 自動調整によりレイテンシーとストップが減少
  • リソース: CPUコアとRAMがスレッドの適切なサイズを決定する
  • ワークロード:I/O負荷が高い場合はより多くのスレッドが必要、CPU負荷が高い場合はより少ないスレッドで済む
  • チューニング: 小さな、的を絞ったパラメータは、一律の値よりも強い効果を発揮します。

スレッドプールアーキテクチャの比較

私はまず 建築, チューニングの限界を定義するから。Apache は接続ごとにプロセスやスレッドを使うから、RAM を多く消費して、ピーク時のレイテンシが増えるんだ [1]。NGINX と LiteSpeed はイベント駆動型モデルを採用していて、少数のワーカーが多くの接続を多重化してる。これによりコンテキストの切り替えが減って、オーバーヘッドが下がるんだ [1]。 テストでは、NGINX は 6,025.3 リクエスト/秒を処理し、Apache は同じシナリオで 826.5 リクエスト/秒、LiteSpeed は 69,618.5 リクエスト/秒でトップとなりました [1]。アーキテクチャの比較についてさらに詳しく知りたい方は、以下のリンクで詳細情報をご覧いただけます。 Apache 対 NGINX, 私が最初の分類に使用するものです。.

また、各エンジンがブロックするタスクをどのように処理するかも重要です。NGINX と LiteSpeed は、非同期インターフェースと限定的な補助スレッドを介して、イベントループをファイルシステムまたはアップストリーム I/O から切り離しています。Apache は、従来のモデルでは、接続ごとに 1 つのスレッド/プロセスをバインドします。MPM event を使用すると、Keep-Alive の負荷を軽減できますが、接続ごとのメモリフットプリントは依然として高くなります。 実際には、同時接続の低速クライアントや大容量のアップロードが多いほど、イベントモデルが効果を発揮します。.

セルフチューニングの実際の仕組み

最新のサーバーは、 スレッド-多くの場合、自動的に。コントローラは短いサイクルで負荷を検証し、現在の値と過去の値を比較して、プールサイズを拡大または縮小します [2]。 キューが滞った場合、アルゴリズムはサイクルを短縮し、処理が再び安定するまでスレッドを追加します [2]。これにより、介入の手間が省け、過剰な割り当てが防止され、ヘッド・オブ・ライン・ブロッキングの可能性が低くなります。参考として、Open Liberty のセルフチューニングコントローラの動作を記載した文書があります。この文書では、その仕組みが明確に説明されています [2]。.

私はその際に3つの調整レバーに注意を払っています。 ヒステリシス フラッピング(スパイクごとに即座に反応しない)対策、 ハードキャップ RAMオーバーフローや 最小サイズ, 、ウォームアップコストがバーストごとに発生しないようにするためです。また、個別の目標値を設定することも有効です。 アクティブ スレッド (coreThreads) 対 最大スレッド (maxThreads)。これにより、アイドル状態でリソースを消費することなく、プールをアクティブな状態に保つことができます [2]。共有環境では、Web サーバーが隣接サービスに対して CPU スロットを過度に占有しないように、拡張率を抑制しています [4]。.

ベンチマークからの指標

実際の価値は、以下の点で役立ちます。 決断. バーストシナリオでは、NGINX は非常に低いレイテンシと高い安定性で高い評価を得ています [3]。極端な並行処理では、Lighttpd がテストで 1 秒あたりのリクエスト数が最も多く、OpenLiteSpeed と LiteSpeed がそれに続いています [3]。 NGINX は最大 123.26 MB/秒の大容量ファイル転送に成功し、OpenLiteSpeed はそれに僅差で続いています。これは、イベント駆動型アーキテクチャの効率性を強調する結果です [3]。私は、このような指標を使用して、スレッドの調整が実際に有益である部分と、アーキテクチャに起因する制限がある部分を評価しています。.

サーバー モデル/スレッド 例レート 核心的なメッセージ
アパッチ 接続ごとのプロセス/スレッド 826.5 リクエスト/秒 [1] フレキシブル, 、しかしRAMの必要量が多い
NGINX イベント + 少数のワーカー 6,025.3 リクエスト/秒 [1] 低い レイテンシー, 、倹約的
ライトスピード イベント + LSAPI 69,618.5 リクエスト/秒 [1] とても 速い, 、GUIチューニング
ライトトピーディー イベント + 非同期 28,308 リクエスト/秒 (高並列) [3] スケーリング ヒント 上々

この表は相対的な メリット, 、確固たる約束はありません。私は常に、自身のワークロードのコンテキストで評価しています。短い動的な応答、多数の小さな静的ファイル、または大きなストリームなどです。 差異は、ネットワーク、ストレージ、TLS オフロード、PHP 設定に起因する場合があります。そのため、CPU スティール、ランキューの長さ、ワーカーあたりの RSS などのメトリックをスレッド数と相関させています。この視点によって初めて、真のスレッドのボトルネックと I/O またはアプリケーションの制限を区別することができます。.

信頼性の高い数値を得るために、ランプアップフェーズを利用し、p50/p95/p99レイテンシを比較しています。 急勾配のp99曲線 p50 値が一定の場合、純粋な CPU 飽和よりもキューイングの可能性が高いことを示唆しています。また、クローズド(コンカレンシーのみ制御)の負荷プロファイルよりも、オープン(RPS 制御)の負荷プロファイルの方が、システムがリクエストをアクティブにドロップし始めるポイントをより明確に示します。これにより、スレッドの増加が効果的でなくなり、バックプレッシャーやレート制限の方がより適切であるポイントを定義することができます。.

実践:ワーカーと接続の寸法決定

私はまず CPU-コア:worker_processes または LSWS ワーカーはコアを上回ってはならず、そうしないとコンテキストの切り替えが増加します。NGINX の場合、接続とファイルディスクリプタの合計が ulimit‑n 未満になるように worker_connections を調整します。 Apache では、RSS が子プロセスごとに RAM を急速に消費するため、MaxRequestWorkers を高く設定することは避けています。LiteSpeed では、PHP プロセスプールと HTTP ワーカーのバランスを保ち、PHP がボトルネックにならないようにしています。エンジン間の速度の違いを理解したい方は、以下の比較が参考になるでしょう。 ライトスピードとアパッチの比較, 私がチューニングの背景として使用しているものです。.

簡単な目安としては、まずFD予算(ulimit-nからログ、アップストリーム、ファイル用の予備を差し引いたもの)を計算し、それをワーカーあたりの同時接続予定数で割って、HTTP + アップストリーム + TLSバッファの合計に十分かどうかを確認します。 その後、バックログキューを適度にサイズ設定します。バーストに対応できるほど大きく、過負荷を隠さないほど小さくします。最後に、リクエストパターンに合わせてキープアライブの値を設定します。アセットの多い短いページはタイムアウトを長く設定し、接続あたりのリクエストが少ない API トラフィックは値を低く設定します。.

高負荷のための LiteSpeed の微調整

LiteSpeedでは、私は以下を重視しています。 LSAPI, コンテキストの切り替えを最小限に抑えるためです。CHILD プロセスが限界に達していると判断したら、LSAPI_CHILDREN を 10 から 40 へ、必要に応じて 100 まで段階的に増やします。その際には、CPU および RAM のチェックも同時に行います [6]。 GUI により、リスナーの追加、ポートの解放、転送、.htaccess の読み込みが容易になり、変更が迅速に行えるようになりました [1]。継続的な負荷の下では、大きな変化ではなく小さな変化の効果をテストし、レイテンシのピークを早期に検出しています。 共有環境では、他のサービスが CPU を消費している場合に coreThreads を減らして、セルフチューナーがアクティブなスレッドをあまり多く保持しないようにしています [2][4]。.

さらに、リスナーごとのキープアライブと HTTP/2/HTTP/3 の使用状況を監視しています。マルチプレキシングは接続数を減らしますが、ソケットごとのメモリ要件は増えます。 そのため、送信バッファは控えめに設定し、純益が明らかな場合(テキストの応答が多く、CPU の制限がほとんどない場合)にのみ圧縮を有効にしています。大きな静的ファイルについては、ゼロコピーメカニズムを利用し、同時ダウンロードスロットを制限することで、トラフィックのピーク時に PHP ワーカーが処理に追いつかないという事態を防いでいます。.

NGINX:イベントモデルを効率的に活用する

NGINX では、worker_processes を 自動車 またはコア数。epoll/kqueue、アクティブな accept_mutex、および調整されたバックログ値を使用して、接続の受け入れを均等に保ちます。アイドル状態のソケットが FD プールを詰まらせないように、keepalive_requests および keepalive_timeout を適切に設定するようにしています。 大きな静的ファイルは、sendfile、tcp_nopush、および適切な output_buffers を使用して転送します。レート制限および接続制限は、ボットやバーストによってスレッドプールが間接的に負荷がかかる場合にのみ使用します。これは、スロットルごとに追加のステート管理が発生するためです。.

プロキシシナリオでは、 アップストリーム・キープアライブ 重要:低すぎると接続確立の遅延が発生し、高すぎるとFDがブロックされます。バックエンドの容量に適した値を選択し、connect/read/send のタイムアウトを明確に分離して、欠陥のあるバックエンドがイベントループを拘束しないようにしています。 reuseport とオプションの CPU アフィニティを使用して、NIC の IRQ/RSS 設定がサポートしている限り、コア間で負荷をより均等に分散します。HTTP/2/3 では、個々の大きなストリームが接続全体を支配しないように、ヘッダーとフロー制御の制限を慎重に調整します。.

Apache: MPM event を正しく設定する

Apacheでは、私は以下を使用しています。 イベント prefork の代わりに、Keep‑Alive セッションがワーカーを永続的に拘束しないようにします。MinSpareThreads と MaxRequestWorkers は、コアあたりの実行キューが 1 未満になるように設定します。 ThreadStackSize は、利用可能な RAM に多くのワーカーが収まるように小さく設定しています。ただし、小さすぎるとモジュールでスタックオーバーフローが発生するリスクがあります。適度な KeepAlive タイムアウトと制限された KeepAliveRequests により、少数のクライアントが多くのスレッドをブロックすることを防ぎます。PHP は PHP‑FPM または LSAPI に移行し、Web サーバー自体を軽量に保ちます。.

また、ServerLimit、ThreadsPerChild、MaxRequestWorkers のバランスにも注意しています。この 3 つが、実際に生成できるスレッドの数を決定します。 HTTP/2 では、適度なストリーム制限を持つ MPM イベントを使用しています。値が高すぎると、RAM 消費量とスケジューラコストが上昇します。 グローバルキャッシュの大きいモジュールは、必要な場合にのみロードします。プロセスが長時間実行され、メモリが変更されると、コピーオンライトの利点が失われるためです。.

RAMとスレッド:メモリを正確に計算する

を数える。 RSS ワーカー/子プロセスごとに計画された最大数に、カーネルバッファとキャッシュを加算します。バッファが残っていない場合は、スワップによってレイテンシが急増するため、スレッドを削減したり、スワップを増やしたりすることは決してありません。 PHP‑FPM または LSAPI の場合は、Web サーバーと SAPI の合計が安定するように、平均 PHP‑RSS も追加で計算します。TLS 終了コストも考慮に入れます。これは、証明書ハンドシェイクと大きなアウトバウンドバッファが消費量を増加させるためです。RAM のバランスが取れて初めて、スレッドのネジをさらに締め付けます。.

HTTP/2/3 では、接続ごとに追加のヘッダー/フロー制御状態を考慮しています。GZIP/Brotli は、圧縮データと非圧縮データを同時にバッファリングします。これは、リクエストごとに数百 KB の追加容量を意味する場合があります。 また、ログや一時ファイル用の予備も考慮に入れています。Apache では、ThreadStackSize の値を小さくすると密度が高くなりますが、NGINX および LiteSpeed では、主に並列ソケットの数と送受信バッファのサイズが影響します。チューニングの前にすべてのコンポーネントを合計しておくと、後で嫌な驚きに遭遇することを避けられます。.

手動で介入する場合

頼りにしているのは セルフチューニング, 、メトリクスが反対の結果を示すまで。共有ホスティングでマシンを共有している場合は、他のプロセスが十分な CPU 時間を確保できるように、coreThreads または MaxThreads を抑制します [2][4]。プロセスごとに厳しいスレッド制限がある場合は、OS エラーを回避するために maxThreads を控えめに設定します [2]。 デッドロックのようなパターンが発生した場合は、プールサイズを一時的に増やし、キューを観察した後、再びサイズを下げます。測定値と典型的なパターンを比較したい場合は、以下の情報をご参考ください。 ウェブサーバー速度比較, 、私はこれを妥当性チェックとして活用しています。.

介入のシグナルとしては、主に以下を利用しています。CPU負荷が低いにもかかわらず持続するp99のピーク、ソケットキューの増加、急激な増加 TIME_WAIT- 数値や、未決済のFDの急激な増加。このような場合、私はまず、想定(接続/レート制限)を抑制し、タイムアウトでバックエンドを分離し、その後、慎重にスレッドを増やします。これにより、過負荷を内部に移すだけで、すべてのレイテンシーを悪化させることを回避しています。.

よくある間違いと簡単なチェック

私はよく見ます 高い データが流れていないにもかかわらずスレッドをバインドするキープアライブタイムアウト。また、RAM 予算を大幅に超える MaxRequestWorkers や、目標の並列処理には低すぎる ulimit‑n もよく見られます。NGINX では、アップストリーム接続による FD 使用量を過小評価する人が多く、各バックエンドは 2 倍にカウントされます。 LiteSpeed では、PHP プールが HTTP ワーカーよりも早く成長するため、リクエストは受け付けられるものの、処理が遅れてしまいます。短い負荷テスト、ヒープ/RSS の比較、および実行キューを確認することで、私はこれらのパターンを数分で発見することができます。.

また、syn‑backlog が小さすぎて、Web サーバーの前に接続が跳ね返ってしまうこともよくあります。バッファのないアクセスログが、低速のストレージに同期して書き込まれることもあります。デバッグ/トレースログが誤ってアクティブなままになり、CPU を占有してしまうこともあります。 HTTP/2/3 に移行すると、ストリーム制限やヘッダーバッファが大きすぎると、接続あたりのメモリ消費量が増えます。これは、多くのクライアントが少量のデータを転送している場合に特に顕著です。そのため、短い応答と長い応答の分布を確認し、制限を適宜調整しています。.

HTTP/2 および HTTP/3:スレッドプールにとっての意味

マルチプレキシングは、クライアントあたりの TCP 接続数を大幅に削減します。これは FD および Accept コストには良い影響がありますが、接続ごとの状態への負荷を増加させます。そのため、HTTP/2 では同時ストリームの制限を慎重に設定し、フロー制御を調整して、個々の大きなダウンロードが接続を独占しないようにしています。 HTTP/3 では、TCP によるヘッド・オブ・ライン・ブロッキングは発生しませんが、その代わりに、パケットあたりの CPU 負荷が増加します。私は、レイテンシを低く抑えるために、十分なワーカー容量と小さなバッファサイズを設定しています。 いずれの場合も、スレッドやメモリを消費する長すぎるアイドルセッションよりも、適切に活用された、少数の接続と適切なキープアライブ値の方が望ましいと言えます。.

プラットフォームの要素:カーネル、コンテナ、NUMA

仮想化では、次の点に注意しています。 CPUスティール および cgroups の制限:ハイパーバイザーがコアを盗用したり、コンテナが部分コアしか所有していない場合、worker_processes=auto は過度に楽観的である可能性があります。必要に応じて、ワーカーを実際のコアに固定し、その数を 効果的に 利用可能な予算。NUMA ホストでは、Web サーバーはローカルメモリ割り当ての恩恵を受けます。ソケットごとにワーカーをバンドルすることで、不要なクロスノードアクセスを回避しています。レイテンシが重要なワークロードでは、ページフォールトのピークを回避するため、Transparent Huge Pages を無効にすることがよくあります。.

OS レベルでは、ファイルディスクリプタの制限、接続バックログ、およびアウトバウンド接続のポート範囲を制御しています。 実際に必要なものだけを増やし、ロールオーバー時の動作をテストし、セキュリティ制限を厳格に守っています。ネットワーク側では、RSS/IRQ 分配と MTU 設定がトラフィックプロファイルに合っていることを確認しています。そうしないと、パケットの到着が遅すぎる、または NIC キューで滞留してしまうため、Web サーバーのチューニングが無駄になってしまいます。.

推測ではなく測定:テストの実践ガイド

私は、ウォームアップ(キャッシュ、JIT、TLS セッション)、プラトー(安定した RPS/同時実行)、バースト(短いピーク)の 3 段階で負荷テストを実施しています。静的ファイル、API コール、動的ページごとに個別のプロファイルを作成することで、スレッド、I/O、バックエンドの制限要因を個別に確認することができます。 FD 数値、実行キュー、コンテキストスイッチ、プロセスごとの RSS、p50/p95/p99 レイテンシを並行して記録します。目標として、70~85 % の使用率で動作するポイントを選択します。これは、飽和領域で継続的に動作することなく、実際の変動に十分なバッファを確保できる値です。.

意思決定ガイド(要約)

私が選ぶ NGINX, 低レイテンシ、リソースの節約、柔軟な .conf チューニングが可能であることが重要な場合。PHP 負荷が支配的で、GUI によって操作が簡略化され、LSAPI によってボトルネックが軽減される場合は、LiteSpeed を採用しています。モジュールや .htaccess に依存しており、MPM イベント構成を確実に制御できる場合は、Apache を採用しています。 多くの場合、セルフチューニングのメカニズムで十分です。メトリックがハング、ハードリミット、RAM 圧力を示している場合にのみ、介入する必要があります [2]。現実的なコアおよび RAM 予算、小さなステップサイズ、レイテンシカーブの観察により、スレッドチューニングは確実に目標を達成します。.

現在の記事