TCP 輻輳制御

ネットワークの混雑を検知し、送信速度を動的に調整して公平で効率的なデータ転送を実現する TCP の仕組み

ネットワーク

TCP 輻輳制御とは

TCP 輻輳制御 (Congestion Control) は、ネットワークの混雑度を推定し、送信速度を動的に調整する仕組みである。全ての送信者が最大速度でデータを送ると、ルーターのバッファが溢れてパケットロスが発生する。輻輳制御はこの問題を防ぎ、ネットワーク帯域を公平に共有する。

輻輳ウィンドウ (cwnd)

TCP は「輻輳ウィンドウ (cwnd)」で一度に送信できるデータ量を制御する。

送信可能量 = min(cwnd, 受信ウィンドウ)

4 つのフェーズ

1. スロースタート

接続開始時は経路にどれだけ余裕があるか分からないため、cwnd を小さな初期ウィンドウ (IW) から始める。RFC 5681 は IW の上限を最大セグメントサイズ (SMSS) に応じて 2〜4 セグメントと定めており、RFC 6928 はこれを 10 セグメントへ広げる実験的仕様を規定している。その後は ACK が新たに確認したバイト数 N に対して cwnd += min(N, SMSS) で増やすので、ロスが起きない限り 1 RTT ごとにおおよそ倍になる。

RTT 1: cwnd = IW      → IW 分のセグメントを送信
RTT 2: cwnd = IW × 2
RTT 3: cwnd = IW × 4
RTT 4: cwnd = IW × 8

「スロースタート」という名前だが、増え方は指数的なので、数 RTT で回線を埋めるところまで一気に加速する。低速な回線を気遣った命名ではなく、送り始めを小さく抑えるという意味である。

2. 輻輳回避

cwnd がしきい値 (ssthresh) に達すると増え方が切り替わり、RTT ごとに 1 MSS 程度の線形増加になる。ssthresh は「前回ここで詰まった」という記録なので、その付近から先は倍々では踏み込まず、1 セグメントずつ足して上限を探る。

3. パケットロス検知

TCP はロスを 2 つの経路で知る。1 つは同じ ACK 番号が 3 回重複して届く場合で、後続のセグメントは相手に届いているので経路が完全に詰まったとは考えず、抜けた 1 個だけを再送タイマーの満了を待たずに送り直す (高速再送)。もう 1 つは再送タイムアウト (RTO) の満了で、ACK が返ってこない深刻な状態とみなす。RFC 5681 では前者のとき ssthresh を max(FlightSize / 2, 2 × SMSS) に落とし、後者では cwnd を 1 セグメント相当まで落としてスロースタートからやり直す。同じ「ロス」でも罰の重さが違う。

4. 高速回復

高速再送のあとはスロースタートに戻らず、cwnd を ssthresh 付近 (おおよそ転送中だったデータ量の半分) まで下げて輻輳回避フェーズへ移る。重複 ACK が届いていること自体が経路がまだ流れている証拠なので、ゼロから積み直す必要がないという判断である。ロスが無ければ RTT ごとに 1 MSS 加算、ロスがあれば掛け算で削るというこの組み合わせを AIMD (加算的増加・乗算的減少) と呼び、同じ経路を共有する複数の接続の取り分が公平な配分へ収束する根拠になっている。

フロー制御との違い

輻輳制御と混同されやすいのがフロー制御だ。フロー制御は「受信側が処理しきれない量を送らない」ための仕組みで、受信側が広告する受信ウィンドウに従う。一方の輻輳制御は「ネットワーク経路の混雑を悪化させない」ための仕組みで、送信側が cwnd を自律的に調整する。守る相手が受信側かネットワークかという違いで、実際の送信量は両者の小さい方で決まる。

アルゴリズムの進化

改良の歴史は「ロスをどう解釈し、どれだけ削るか」の変遷として読むと分かりやすい。

アルゴリズム年代特徴
Tahoe1988ロスを検知すると初期ウィンドウまで戻してスロースタートから再開
Reno1990高速再送・高速回復を導入し、削る量を 50% に留める
CUBIC2008直前の輻輳イベントからの経過時間の 3 次関数で増やし、削る量は 30%。Linux・Windows・Apple の既定
BBR2016ロスではなく配送レートと RTT の測定値から経路をモデル化

CUBIC の要点は、増加の速さが RTT に依存しないことである。従来方式は RTT が短い接続ほど cwnd を速く増やせたため、遠距離の接続が近距離の接続に押し負けた。CUBIC は最後にロスが起きた時点からの経過時間を変数に取るので、RTT の長短で不利になりにくい。仕様は RFC 9438 (2023 年 8 月) として標準化されている。

BBR (Bottleneck Bandwidth and Round-trip propagation time) は、ボトルネックの配送レートと最小 RTT を測り続けて経路の明示的なモデルを作り、送信レートと転送中のデータ量をそのモデルに合わせる。ロスを輻輳の唯一の合図として扱わないので、ランダムなロスが混じる経路や浅いバッファでもスループットが落ちにくく、深いバッファでは行列を作らないぶん待ち時間の膨張 (bufferbloat) を避けられる。ただし 2026 年 8 月時点では IETF の Internet-Draft の段階で、RFC としては発行されていない。

Web パフォーマンスへの影響

新規接続の立ち上がりの遅さ

新しい TCP 接続は必ずスロースタートから始まるので、開いた直後は回線に余裕があっても大きなファイルを一気に送れない。HTTP/2 が効くのはここで、1 本の接続を多重化して使い回せば、すでに加速し終えた cwnd をそのまま次のリクエストに使える。一方 HTTP/3 (QUIC) の 0-RTT が省くのは接続確立の往復であって、cwnd の立ち上がりは省けない。QUIC も RFC 9002 で輻輳制御を規定しており、スロースタートで加速する過程は TCP と同じように残る。

CDN の効果

CloudFront のエッジロケーションはユーザーに近いため、RTT が短い。RTT が短いとスロースタートの加速が速く、cwnd が早く大きくなる。CDN がパフォーマンスを改善する理由の 1 つだ。

Lambda + API Gateway での考慮

Lambda の実行環境は呼び出しをまたいで再利用されるため、ハンドラーの外で作った接続は同じ実行環境が処理する後続の呼び出しでも使い回せる。AWS もこの再利用を前提に、SDK クライアントやデータベース接続をハンドラーの外で初期化することを推奨している。落とし穴はアイドル接続で、Lambda は使われていない接続を時間の経過とともに破棄するので、実行環境が生きていても切れた接続を掴んでエラーになる。ランタイムの keep-alive 指定で持続的な接続を維持するのが定石である。RDS Proxy の役目は接続プールの共有で、接続ごとの認証情報の処理と安全な接続の確立にかかる負荷を肩代わりし、同時実行数の急増でデータベースの接続枠を食い潰す事故を防ぐ。TCP のスロースタート自体が消えるわけではなく、接続を作り直す回数が減るぶん立ち上がりの遅さを踏む頻度が下がる、という理解が正しい。

より深く学ぶには関連書籍が役立つ。

この記事は役に立ちましたか?

関連用語

関連する記事