Google · TCP 拥塞控制 · ACM Queue 2016 · CACM 2017 Google Research

丢包≠堵车

BBR 给 TCP 换了一套神经系统——不去撞墙,直接测量瓶颈。三十年「丢包即拥塞」的假设,被两个数推翻。

作者 Neal Cardwell · Yuchung Cheng · C. Stephen Gunn · Soheil Hassas Yeganeh · Van Jacobson
发表 ACM Queue Vol.14 No.5, 2016 转载 CACM Vol.60 No.2, 2017 Linux 4.9+ QUIC 支持

序 · 困境

你在机场连上 Wi-Fi,信号满格,网页十秒打不开

那根光纤能跑 10Gbps——但 TCP 自己把路堵死了。1988 年 Van Jacobson 设计拥塞控制时,路由器只有几 KB 缓冲区,丢包确实等于拥塞。三十年过去,网卡 Gbps、内存 GB,丢包和拥塞的关系早已断裂——但 TCP 还在靠撞墙判断路况。

当瓶颈缓冲区大时,基于丢包的 CUBIC 不停往里塞,塞到溢出才停——队列装满,延迟爆炸。这叫 bufferbloat。
当瓶颈缓冲区小时,随机无线丢包被误判为拥塞——吞吐暴跌。

BBR 问了一个不同的问题:不去看拥塞的后果(丢包),直接去测量拥塞发生的那个位置——瓶颈链路的容量和传播延迟。

01 · 核心机制

两个数,描出一条路

CUBIC 靠撞墙(丢包)判断路况,BBR 直接测量道路本身:最快能跑多快?路有多长?

  1. BtlBw瓶颈带宽 · Bottleneck Bandwidth

    过去 10 个往返内网络交付数据的最大速率。不是发多快,是对方收多快。那个峰值就是这条路的速度上限。每次 ACK 回来都产生一个速率采样,窗口最大滤波器自动跟踪上限。

  2. RTprop往返传播时间 · Round-Trip Propagation Time

    数据在空路上往返的最短时间——物理距离的耗时,不含排队。过去 10 秒内的最小 RTT。只要路上有排队,RTT 就会被抬高;只有把队列清空时测到的才是真正的传播延迟。

  3. BDP带宽延迟积 · Bandwidth-Delay Product

    BDP = BtlBw × RTprop,恰好填满管道的数据量。1979 年 Kleinrock 就证明这是吞吐与延迟同时最优的操作点:再多一点,数据开始排队,延迟变坏;再少一点,带宽浪费。

  4. 那条著名曲线Figure 1 · RTT / Delivery Rate vs Inflight

    横轴是空中数据量(inflight),纵轴是 RTT 和交付速率。inflight < BDP 时速率随 inflight 线性增长,RTT 恒定(没排队)。inflight > BDP 时速率不再增长,RTT 线性攀升(开始排队)。继续塞到爆 → 丢包。BBR 要做的就是让 inflight 始终逼近 BDP 那个点:吞吐最大、延迟最小——两个目标同时达到。

02 · 状态机

四步找到并守住那个最佳点

BBR 用四个状态在 BDP 最优操作点附近巡航。每个状态解决一个具体问题。

STARTUP 起步

像新人走进空高速,油门踩到底——但用交付速率来判断是否到了瓶颈,而不是等撞墙。

增益 2/ln2 ≈ 2.89

DRAIN 排空

起步太快积了一堆排队数据。这时减速,把队列慢慢放掉,让 inflight 回到 BDP 附近。

增益 0.35

PROBE_BW 巡航

大部分时间按瓶颈速率发送。每隔 8 个往返穿插一次加速探测(1.25×)和一次让路(0.75×),持续发现新带宽。

周期 [1.25, 0.75, 1×6]

PROBE_RTT 重测

超过 10 秒没刷新 RTprop?主动把 inflight 降到 4 个包,强制清空所有队列,重新量一遍真正的传播延迟。只需 200ms。

inflight → 4 包

03 · 设计哲学

BBR 翻转了四个网络默认假设

这些原则不仅适用于拥塞控制,也是自适应系统的通用思维。

  1. 01
    测量代替猜测 CUBIC 靠丢包事件的频率间接推断拥塞。BBR 直接测量瓶颈带宽和传播延迟——在事件发生之前就知道系统状态。
  2. 02
    模型驱动而非事件驱动 维护一个持续更新的路径模型(BtlBw + RTprop),行为锚定在模型上,而不是被动响应丢包和超时。
  3. 03
    吞吐与延迟可以兼得 在 BDP 点,吞吐最大同时延迟最小。之前的拥塞控制认为二者是矛盾——BBR 证明这个矛盾是虚构的。
  4. 04
    排队不是资源,是债 缓冲区不是吞吐的来源——带宽才是。队列只是延迟。把 inflight 控制在 BDP 以内,不需要大缓冲区。

04 · 实绩

YouTube 全球部署后的效果

BBR 不仅更快,而且不堵。Google 在全球最大的视频平台上验证了这套算法。

+4% 全球平均吞吐提升
+14% 部分国家吞吐提升
33× RTT 比 CUBIC 低
+40% 印度 2G/3G 吞吐中位数提升
0.2% 丢包率(CUBIC 靠丢包驱动)

05 · 洞见

这篇论文真正找到的思想结晶

拥塞控制的正确信号不是丢包,是瓶颈本身。测量它,适应它,而不是等它溢出之后才反应。把「拥塞控制」从「损失控制」变回「拥塞控制」——这是对三十年网络直觉的一次校正。

06 · 启发

这篇论文的思想可以带到哪里

BBR 的核心思维——持续测量环境上限,行为锚定在测量结果上——是一种通用的自适应策略。

  1. 迁移API 限流 · 连接池 · 消息队列

    任何需要自适应速率的系统:与其在收到 429 或超时后退避,不如主动测量服务端的处理速率上限,把请求速率锚定在那个值附近。记录每次请求完成时间,维护窗口内完成速率最大值和延迟最小值,在速率拐点处停下。

  2. 混搭应用层并发自调节器

    请求处理的最佳并发数 = 处理速率 × 平均延迟(应用层的 BDP)。做一个运行时自调节器:持续测量处理吞吐和平均延迟,自动调整并发度到乘积附近。偶尔多开一点探测上限(PROBE_BW 思想),偶尔少开一点让队列排空。

  3. 反转排队不是资源,是债

    别在应用层乱加队列——消息队列缓冲、线程池工作队列、数据库写入缓冲。每加一层队列,就加了延迟,但没有加吞吐。用背压替代队列,让上游在源头减速,而不是在下游排队。

07 · FAQ

你可能想问

BBR 和 CUBIC 的核心区别是什么?
CUBIC 是丢包驱动的:靠丢包事件推断拥塞,在缓冲区满和半满之间振荡。BBR 是模型驱动的:直接测量瓶颈带宽和传播延迟,在 BDP 最优操作点附近巡航——延迟低、吞吐高、不靠丢包。
BBR 在丢包严重的无线网络上表现如何?
这是 BBR 相比丢包算法的最大优势之一。CUBIC 碰到无线随机丢包会误判为拥塞并降速;BBR 不把丢包当拥塞信号,所以在丢包率高达 20% 的网络上依然能跑满带宽。
如何在 Linux 上启用 BBR?
echo bbr > /proc/sys/net/ipv4/tcp_congestion_control(需要 Linux 4.9+)。也可以持久化:net.core.default_qdisc = fq + net.ipv4.tcp_congestion_control = bbr 写入 /etc/sysctl.conf。BBR 要求配合 fq(Fair Queue)qdisc 使用。
BBR 有几个版本?
BBRv1(2016,本文):纯模型驱动,不看丢包。BBRv2(2019):加入丢包和 ECN 信号,改善与 CUBIC 的公平性。BBRv3(2023):修复 v2 两个 bug,性能调优。还有 BBR.Swift,为数据中心内链路优化。
BBR 的缺点是什么?
v1 与 CUBIC 共存时公平性有争议——在某些场景下 BBR 会挤占 CUBIC 流的带宽。v2/v3 已大幅改善。此外,BBR 在蜂窝等剧烈波动环境中的表现不如专用方案。

更多技术细节见 Google Research 论文页github.com/google/bbr