跳转到内容

BBR 2017 — 用瓶颈带宽和最小 RTT 替代丢包当拥塞信号

是什么

BBR(Bottleneck Bandwidth and RTT)是 Google 2016 年发表、2017 年进 CACM 的一套 TCP 拥塞控制算法。它解决的问题是:1980 年代以来,TCP 一直把丢包当成网络拥塞的信号,但今天的链路有大缓冲、有无线丢包、还有跨洲长肥管道——丢包既来得太晚(缓冲已经被塞满才丢)又来得太早(无线偶尔丢和拥塞无关)。

BBR 换了一个思路:直接测两个物理量——瓶颈链路的带宽 BtlBw、不排队时的最小往返时间 RTprop,并把发送速率精确控制在 BtlBw、在途数据量精确控制在 BDP = BtlBw × RTprop。这样既跑满带宽又不让数据在中间路由器排队

日常类比:高速路上一段单车道的瓶颈每秒只能过 100 辆车。CUBIC 司机的策略是疯狂加车直到撞车(丢包)再退一半,路上排起长队他还以为没满;BBR 司机先测出这段每秒能过 100 辆,再测出畅通时跑完全程要多久,然后只放 100 辆 × 时长那么多车上路——刚好把路填满,但不让任何一辆排队。

为什么重要

不理解 BBR,下面这些事都解释不清:

  • 为什么 YouTube 在 2016 年切到 BBR 后,全球平均吞吐提升 4%、跨洲提升 14%——同样的链路,CUBIC 没用满
  • 为什么 Google B4 内部 WAN、GCP 公网出口、Spotify、Dropbox 都切到 BBR——大缓冲场景丢包派彻底失灵
  • 为什么 Linux 4.9(2016 年 12 月)开始内置 BBR,而 BBR 至今(2026)依然没把 cubic-2008 完全替代——公平性争议没消
  • 为什么 2017 年之后所有拥塞控制论文都在和 BBR 比——它把”丢包等于拥塞”这个 30 年的默认假设撕了

核心要点

BBR 的所有秘密都在两个量加一台状态机里:

BtlBw = max(deliveryRate) 在最近 10 个 RTT 窗口里
RTprop = min(RTT) 在最近 10 秒窗口里
速率上限 pacing_rate = pacing_gain × BtlBw
在途上限 cwnd = cwnd_gain × BtlBw × RTprop

关键观察来自 Leonard Kleinrock 1979 年的论断:带宽利用率最高、排队延迟最低的工作点,正好是 BDP = BtlBw × RTprop。CUBIC 拼命堆窗口直到撞墙,BBR 直接奔这个工作点去。

BBR 跑一台四状态机:

  1. STARTUP:刚连上时不知道 BtlBw 多大,按 pacing_gain = 2/ln2 ≈ 2.89 指数增长,连续三个 RTT 测到的带宽不再涨,认为已经把瓶颈喂饱
  2. DRAIN:STARTUP 跑得猛、可能在路由器里堆了一些队,按 pacing_gain = ln2/2 ≈ 0.35 减速直到 inflight ≤ BDP
  3. PROBE_BW:稳态。8 个 RTT 一轮,pacing_gain 依次取 [1.25, 0.75, 1, 1, 1, 1, 1, 1]——先多发 25% 探一下带宽涨没涨,再少发 25% 把多发的排空,剩下 6 个 RTT 持平
  4. PROBE_RTT:每 10 秒一次,把在途砍到 4 个包持续 200ms,让队列清空,重测 RTprop——避免被长流的高占用蒙蔽

最后一招叫 pacing:BBR 不是一收 ACK 就把允许的所有包扔出去,而是按 pacing_rate 平均时间间隔发——相当于把车一辆辆均匀放到路上,而不是一次性放一队。

实践案例

案例 1:在自己机器上启用 BBR

Terminal window
# 看可用算法
sysctl net.ipv4.tcp_available_congestion_control
# = reno cubic bbr
# 切到 BBR(Linux 4.9+)
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
sudo sysctl -w net.core.default_qdisc=fq # BBR 必须配 fq 队列做 pacing
# 看一条连接当前的 BBR 内部状态
ss -tin state established | head
# bbr wscale:7,7 rtt:23.4/2.1 mss:1448 pacing_rate 1245Mbps delivery_rate 988Mbps

如果机器是 Linux 4.9 之后(含绝大多数云服务器、CDN 节点),换两行 sysctl 就能切到 BBR。海外回国的长 RTT 链路上,BBR 对 CUBIC 通常有 3 到 10 倍吞吐提升。

案例 2:手算 BDP 和工作点

假设家用宽带上行 100 Mbps、到东京机房畅通 RTT 60ms:

BtlBw = 100 Mbps = 12.5 MB/s
RTprop = 60 ms = 0.06 s
BDP = 12.5 × 0.06 = 0.75 MB ≈ 750 KB ≈ 500 个 1500 字节的包

BBR 把 cwnd 控制在 1500 KB(cwnd_gain = 2,给 ACK 回程留余量),pacing_rate 控制在 100 Mbps。这条流:链路喂满、路由器队列里几乎没有它的包——延迟保持在 60ms 左右。

同样链路上跑 CUBIC:cwnd 会持续涨到撞缓冲上限(路由器可能有 100MB 缓冲),实际 RTT 飙到 600ms,下载是快了一点点,但游戏、视频会议同时全卡——这就是 buffer bloat(缓冲区膨胀)。

案例 3:BBR 怎么扛丢包

无线链路丢包 1%:

  • CUBIC:每次丢包砍 cwnd 到 70%,长期吞吐被压到几 Mbps(公式 ≈ MSS / (RTT × √loss),1% 丢包就废)
  • BBR:丢包不直接触发降速,只要 deliveryRate 还在,BBR 就继续按 BtlBw 发——丢的那部分在重传里补回来。Google 测过 BBR 在 1% 丢包下跑 CUBIC 的 100 倍吞吐

这一点让 BBR 成为越洋链路、4G/5G 移动网络、卫星链路的天选方案。

踩过的坑

  1. BBRv1 对 CUBIC 不公平:同一条瓶颈,BBR 流和 CUBIC 流共存时,BBR 容易把 CUBIC 挤到很小。论文里没强调,社区测出来后吵了很久。Google 2019 年发的 BBRv2 加了显式的 inflight 上限和丢包反馈缓解这点
  2. 必须配 fq 队列:BBR 的 pacing 需要内核排队规则支持,没切 fq 就退化成”一次发一队”,实测吞吐反而下降。换 BBR 必须同时换 qdisc
  3. PROBE_RTT 太激进:每 10 秒把在途砍到 4 个包,突发性把流速降到几乎 0,对短交互(在线游戏的关键时刻)会有可观察的卡顿。BBRv2 改成更平滑的探测
  4. 多流共享瓶颈时 RTprop 会被高估:A 和 B 都在跑 BBR,B 的存在让 A 测到的最小 RTT 永远带着 B 的排队——A 以为的 RTprop 比真实大,发得比该发的多。这个问题在 ECN(显式拥塞通知)+ BBRv2 才彻底修
  5. deliveryRate 测不准时崩:BBR 假设 ACK 流回得稳定。链路上有 ACK 聚合(4G 基站典型行为)、或者中间设备做 TCP 加速代理把 ACK 攒批回,BBR 就以为带宽时高时低,pacing 抖到不可用。需要内核打开 tcp_no_metrics_save 配合策略路由
  6. 慢启动期间过冲:STARTUP 阶段 pacing_gain ≈ 2.89 是为快速找到 BtlBw 设计的,但浅缓冲链路上这倍率会瞬间触发大量丢包。Google 内网用得舒服是因为他们路由器缓冲足够深,公网部署常见配套调小起始 cwnd

适用 vs 不适用场景

适用

  • 长肥管道(高带宽 × 高 RTT):跨洲数据传输、CDN 回源、视频流分发
  • 高丢包链路:4G/5G、卫星、Wi-Fi 拥挤的咖啡馆
  • 大缓冲设备:家用路由器、运营商接入网(buffer bloat 重灾区)
  • 内部 WAN:Google B4 实战验证

不适用

  • 浅缓冲数据中心内(µs 级 RTT):DCTCP / cubic-2008 用 ECN 更精准
  • 实时音视频要求<10ms 延迟:BBR 的 PROBE_RTT 周期性扰动不可控,专用 SCReAM / NADA 更合适
  • 与 CUBIC 共存且公平性敏感的场景(旧网站、ISP 对外):等 BBRv2/v3 部署稳定再切

历史小故事(可跳过)

  • 1979 年:Leonard Kleinrock 提出”最优工作点 = BDP”的理论结论。当时硬件测不出 BtlBw 和 RTprop,结论被当数学游戏束之高阁
  • 1988 年jacobson-1988 提出 Tahoe / Reno,用丢包当拥塞信号——理由是当年的 9600 bps 链路缓冲极小,丢包就等于拥塞。这套假设统治了 30 年
  • 2014 年:Yuchung Cheng、Neal Cardwell 在 Google 注意到 YouTube 在跨洲长肥链路上拿不满带宽,开始重读 Kleinrock 1979
  • 2016 年 9 月:BBR 上 ACM Queue,8 个月后进 Linux 主线(4.9)。Van Jacobson 是合作者之一——同一个人 28 年后亲手写下了否定自己 1988 算法的下一代
  • 2019 年:BBRv2 在 IETF 发布;2023 年 BBRv3 解决公平性。至今仍在演进

学到什么

  1. 测量比假设值钱:丢包是个间接信号,BtlBw 和 RTprop 是直接信号。能测就别推
  2. 状态机化拥塞控制:把”现在该探测 / 该排空 / 该巡航”显式拆成阶段,比”一根公式包打天下”更鲁棒
  3. 30 年的默认假设是会被推翻的:1988 年的硬件约束在 2016 年早不成立,但教科书还在教。问”这个假设今天还成立吗”比死记算法重要
  4. 理论到产品要等硬件:Kleinrock 1979 的结论等了 37 年才落地——不是理论不对,是当时没法测
  5. pacing 是被低估的工程细节:把”允许发”和”立刻发”分开,让 TCP 的发送行为从”突发列车”变成”连续水流”,仅这一项就足以让相同 cwnd 下尾延迟显著下降
  6. 公平性是大规模部署的最后一关:算法在自己流之间公平不够,跨算法(BBR vs CUBIC)公平才决定能否成为默认。BBRv1 → v2 → v3 的演进基本就在修这一项

延伸阅读

关联

  • jacobson-1988 —— 30 年丢包派范式,BBR 是它的反题
  • cubic-2008 —— Linux 默认拥塞控制;BBR 共存公平性问题的对手
  • tcp-vegas-1995 —— 第一个用 RTT 当信号的尝试,被丢包派压制;BBR 是它精神上的继承者
  • clark-1988 —— 早期网络架构原则;端到端测量思想的源头
  • tcp —— 协议主条目,BBR 是其拥塞控制层的最新替代

一句话总结

BBR 把”网络拥塞 = 丢包”这个 30 年的默认假设换成”网络拥塞 = 在途数据超过 BDP”,用持续测出来的瓶颈带宽和最小 RTT 直接锁定 Kleinrock 1979 给出的最优工作点——既跑满链路又不堆队列。