拥塞控制:为什么 TCP 一丢包就把速度砍一半
TCP 把丢包当作网络在喊"我满了",收到信号就把发送速率砍一半,然后再一点点往上爬。这不是保守——在一个反馈有延迟的系统里,这是唯一不会崩的做法。
一句话结论
TCP 把丢包当作网络在喊”我满了”。一旦收到这个信号,它立刻把发送速率砍掉一半,然后再一点一点往上爬。
这不是保守,这是在一个反馈有延迟的系统里唯一不会崩的做法。它和调不准淋浴水温是同一条原理 —— 只不过时间尺度从几秒变成了几十毫秒,从你的一只手变成了全世界几十亿台设备。
直觉铺垫:一条你看不见的公路
你对网络几乎一无所知:不知道这条链路有多粗,不知道有多少人和你抢,不知道中间有几个路由器,更不知道它们的队列现在有多长。
你手里唯一的信号是:包丢了没有。
于是策略必须是这样:
- 往上试探时保守 —— 每收到一批确认,只敢多发一点点(加法);
- 往下撤退时果断 —— 一旦发现丢包,直接砍一半(乘法)。
这个不对称就是 AIMD(Additive Increase, Multiplicative Decrease,加性增、乘性减)。
为什么不对称?因为反馈是旧的。你此刻看到的丢包,是一个 RTT(往返时间)之前发生的。等你收到这个信号,网络里的情况早就更糟了。所以撤退必须狠狠地撤,留出安全余量;而往上爬的时候你对容量一无所知,只能一点一点摸。
一句话记住它:加法上去,乘法下来。
推导过程
拥塞窗口
TCP 用一个叫 cwnd(congestion window,拥塞窗口)的数,表示”一次能发出去、不用等确认的数据量”。发送速率大致是:
所以控制 cwnd,就是在控制速度。
AIMD 的两条规则
第一条是加性增:每个往返加 1 个包,慢悠悠地摸天花板在哪里。第二条是乘性减:不管你现在多大,一律对折。
为什么这个不对称是”对”的
设想两条流(A 和 B)抢同一条链路,窗口分别是 和 。
- 加性增让两者等速增加:差值 不变,但比值 朝 1 靠拢 —— 它改善公平。
- 乘性减让两者等比例后退:比值 不变,但差值 变小 —— 它改善收敛。
两条规则交替执行,运行点就同时朝”公平”和”高效”移动,最终稳在两者之间。这就是 AIMD 能被证明收敛到公平的原因 —— 它不是拍脑袋定的,是被推导出来的。
代价:锯齿
AIMD 的代价是 cwnd 永远呈锯齿形:线性爬到丢包 → 砍一半 → 再爬 → 再砍。
锯齿下面积的平均值大约是峰值的 3/4。也就是说,TCP 平均只能跑到链路容量的 75% 左右。剩下那 25% 是它为了”不把网络搞崩”付的保险费。你不可能既零丢包又跑满带宽 —— 想探出容量极限,就必然要撞一次墙。
延迟在哪儿起作用
RTT 就是这条反馈回路的延迟。RTT 越大,两件事同时变坏:
- 反馈越旧,你的反应总是滞后一拍;
- 每 RTT 只加 1 个包,按绝对时间算就爬得越慢 —— 一条跨太平洋的链路要花很久才能把窗口爬起来。
这就是所谓”长肥管道”问题,也是后来 BBR 这类”不看丢包、改看延迟与带宽积”的算法会出现的原因。
1986 年,互联网真的死过一次
这不是思想实验。1986 年,从劳伦斯伯克利实验室到加州大学伯克利分校 —— 相距约 400 米、中间只有 3 跳 —— 吞吐量从每秒几万比特掉到每秒几十比特,掉了将近三个数量级。
线路没断,设备没坏。是拥塞崩溃:
网络一堵 → 开始丢包 → 大家重传 → 重传让网络更堵 → 丢更多包 → 更多重传……
这是个正反馈,一旦转起来就停不下。最要命的是:链路并没有闲着,它一直满载 —— 只是满载的全是重传的包,真正有用的新数据几乎没有。
1988 年 Van Jacobson 把 AIMD 拥塞控制塞进了 TCP,互联网这才活过来。今天每台设备每秒都在替你做几千次”砍一半”的决定,而你从没配置过它。
交互演示
请依次点这三个按钮,它们各自演示一种真实存在的现象:
- 关掉拥塞控制:窗口一路涨到 120,丢包率冲到 80%,可真正送到的新数据只剩五分之一 —— 带宽全被重传吃光了。这就是 1986 年那场崩溃。
- 超大缓冲(bufferbloat):吞吐漂亮地跑满了 99%,但排队延迟飙到 7 个往返。测速软件说你网速很好,可你打游戏卡得要命 —— 因为包都堵在缓冲区里排队。
- 长 RTT:把反馈延迟从 1 拖到 4,效率从 64% 掉到 50%。延迟越大,这条回路越笨。
常见误解
- “丢包说明线路坏了” —— 绝大多数丢包不是故障,是路由器队列满了主动丢弃。这是网络在给你发信号,不是网络坏了。
- “路由器缓冲越大越好” —— 缓冲过大会得 bufferbloat:吞吐看着满,延迟飙到几百毫秒,所有交互式应用一起变差。缓冲不是越大越好,是要和带宽时延积匹配。
- “TCP 会探出真实容量然后保持住” —— 不会。它永远在”爬到丢包 → 砍半 → 再爬”的锯齿里,平均只跑到 3/4 左右。稳态是一个持续振荡,不是一个固定值。
- “长肥管道把窗口调大就行了” —— 窗口能调,难的是 RTT 大导致爬升慢、反馈旧。这是 BBR 等新算法的动机,不是调个参数能解决的。
- “现在的网络早就不在乎拥塞控制了” —— 恰恰相反。拥塞控制是互联网至今没有崩掉的唯一原因,它藏在操作系统内核里,你从没见过它,但每秒都在救你的命。
延伸引用
- 横切原理 · 反馈:为什么淋浴的水温永远调不准 —— 同一条原理在秒级尺度上的版本,AIMD 的”乘法撤退”和”拧一下等三秒”是一回事
- 计算 · 从哈希到数字签名 —— 哈希 + 私钥 = 不可抵赖(待写)
- 外部:Van Jacobson & Karels, Congestion Avoidance and Control(1988)—— AIMD 的原始论文,也是互联网的救命文档
本文是「白盒」计算系列的一篇。发现错误、或者觉得哪一段没讲清楚,欢迎在 GitHub 上提 PR。