【内核调优 01】sysctl 网络调优原理:BDP 公式与 TCP 窗口推导
2026-08-15 · DevCraft Studio
内核调优原理篇:从带宽延迟积 BDP 公式出发,推导 TCP 窗口缩放、rmem/wmem 自动调优、TIME_WAIT 状态机、BBR 与 CUBIC 差异以及 bufferbloat 的来由,讲清每个 sysctl 参数为什么这么设。
延伸阅读
更多相关攻略推荐:泛域名 SSL 证书自动化避坑:Acme.sh + DNS API 、BGP 是互联网的"高德地图":打开网页时,数据是这样找路的、【优化线路 02】BGP / AS4837 常规线路年付 10 美元、高级玩家玩出花:如何在 VPS 上广播自己的 IP 地址?BYOIP、计算机科学两大难题之一:缓存失效与 CDN 瞬时刷新奥秘。
一、为什么发行版内核的默认网络参数这么"抠门"
你刚拿到一台便宜的国外 VPS,第一件事往往是装好 Nginx、跑通业务,然后却发现:明明买的是 1 Gbps 的端口,从国内拉文件却只能跑出一两百兆;明明是 2 核 4G 的配置,压测一上来就报 connection refused。第一反应通常是"机器太垃圾",但真正的原因十有八九不在硬件,而在 Linux 内核那套为了"省内存、保通用"而设计的保守默认值。
Linux 发行版的维护者面对的是从树莓派到超算的全体设备,他们必须选一个对所有人都"不亏"的折中值。结果就是:默认 socket 缓冲区只有一百多 KB、全连接队列只有 128、本地端口只开放两三千个。这些值在一台只跑 SSH 的嵌入式设备上完全够用,但放到一台扛高并发 Web 服务的 VPS 上,就成了压垮性能的那块最短木板。本文是这套系列的原理篇,专讲"为什么这些参数要这么设"——背后的数学与状态机。至于现成的配置模板、生效命令和验证步骤,详见本系列第 02 篇:/vps-sysctl-tuning-guide-2026。
二、带宽延迟积 BDP:决定你能跑满多少带宽的天花板
要理解为什么缓冲区这么重要,必须先建立一个概念:带宽延迟积(Bandwidth-Delay Product,简称 BDP)。它的物理含义是"在任意时刻,管道里'在途'的数据量上限"。公式很简单:BDP 等于链路带宽(字节/秒)乘以往返时延 RTT(秒)。
举个例子:一条 1 Gbps 的链路,RTT 是 200 毫秒。先把带宽换算成字节:1 Gbps 等于 10的9次方 bit/秒,除以 8 得到约 1.25×10的8次方 字节/秒;再乘以 0.2 秒,得到约 2.5×10的7次方 字节,也就是大约 25 MB。这意味着,要让这条跨国链路被"填满",收发两端各自至少要能缓冲约 25 MB 的数据在路上飞。如果 TCP 窗口只有默认的 64 KiB(约 0.06 MB),管道里永远只装了千分之二,剩下的 99.8% 带宽都被白白浪费掉了。
这就是为什么"调 sysctl 能跑满带宽"的本质:你不是在提升链路速度,而是在把 TCP 窗口撑大到至少能容纳一个 BDP。BDP 越大(链路越快、延迟越高),需要的缓冲区就越大。这也是为什么说"缓冲区不是越大越好"——它的合理上界就是 BDP 的 2 到 3 倍,再大只会浪费内存、推高延迟。
三、TCP 窗口缩放:从 64 KiB 到 1 GiB 的跃迁
早期的 TCP 头部里,窗口字段只有 16 位,理论上限就是 64 KiB,这在当年 10 Mbps 的链路上够用。但到了千兆、万兆时代,64 KiB 连一个 BDP 的零头都不到。于是 RFC 1323(现 RFC 7323)引入了"窗口缩放选项"tcp_window_scaling:发送方和接收方在握手时协商一个缩放因子,把 16 位窗口实际放大 2 的 N 次方倍,理论上限拉到约 1 GiB。
红帽官方文档做过实测:同一条 1 Gbps 链路、1.5 毫秒 RTT 下,开启窗口缩放约能跑 630 Mbps,关闭后直接掉到 380 Mbps,差距接近 40%。现代内核几乎都默认开启 tcp_window_scaling = 1,你要做的只是确认它没被谁关掉。它和后面的 tcp_tw_reuse 一样,都依赖 tcp_timestamps 提供的时间戳来正确判断序列号,所以时间戳也别关。理解这一点,你就明白为什么"窗口缩放"是整套调优的地基。
四、rmem_max 与 tcp_rmem 三档值:自动调优的真相
缓冲区有两个层级。第一层是核心层上限 net.core.rmem_max / wmem_max,单位字节,它是所有协议套接字能申请到的硬天花板。第二层是 TCP 专用层 net.ipv4.tcp_rmem / tcp_wmem,三个值分别是最小、默认、最大。很多人误以为内核会老老实实用"最大"那档,其实从内核 2.6.17 起,TCP 就支持自动调优(autotuning):内核会依据实际测量的吞吐,在"最小"和"最大"之间动态滑动接收窗口。
这让调优变得简单:你一般只需把 tcp_rmem 的"最大"撑到 BDP 的 2 到 3 倍,内核会自动在合理区间里取最优值,根本不用把窗口"钉死"。典型取值是 tcp_rmem = 4096 87380 16777216,其中 16777216 即 16 MB。但要记住一个铁律:tcp_rmem 的"最大"永远突破不了 rmem_max 这个天花板,所以两者必须同时上调,只改一个等于白改。这个"两层天花板"模型,是读懂任何一份 sysctl 模板的关键。
五、TIME_WAIT 状态机器:端口耗尽为什么会发生在你身上
TCP 不是"说完就散",主动关闭连接的一方会进入 TIME_WAIT 状态,默认要等 2×MSL(最长报文段寿命,通常约 60 秒)才释放端口。这是 TCP 协议的可靠设计:确保迟到的重复报文不会污染新连接。但代价是,在短连接密集的服务上(比如对外疯狂调 API、爬虫、反向代理),几千个连接一打,本地端口瞬间被 TIME_WAIT 占满,新连接直接报"Cannot assign requested address"。
解法 tcp_tw_reuse = 1 允许在时间戳开启的前提下,把 TIME_WAIT 套接字安全地复用于新的"出站"连接。它只作用于出站,对入站服务端无效——那是应用层 SO_REUSEADDR 或 keep-alive 的事。另一个参数 tcp_tw_recycle 听起来更猛,但它早在 4.12 内核就被删除,且在 NAT / 负载均衡后会造成大面积连接失败,绝对不要用。理解状态机,你才知道 reuse 为什么"相对安全"而 recycle "绝对危险"。
六、从 CUBIC 到 BBR:拥塞控制的范式转移
传统拥塞控制算法 CUBIC 是"基于丢包"的:它把丢包当作网络拥堵的唯一信号,一旦丢包就猛踩刹车、把发送窗口减半。在跨国高延迟、偶发丢包的链路上,这种"宁可错杀"的策略会让吞吐长期跑不到瓶颈带宽。BBR(Bottleneck Bandwidth and RTT)则是 Google 提出的"基于模型"的算法:它主动探测瓶颈带宽和最小 RTT,建立网络模型后直接按模型发送,不盲从丢包信号。
本质区别在于:CUBIC 看到丢包就退让,BBR 看到丢包先判断是不是真拥堵。所以在有损国际链路上,BBR 往往能把吞吐拉到接近物理上限,而 CUBIC 会卡在远低于上限的位置。BBR 从内核 4.9 起合入主线,启用它需要两步:把发包队列规则设为 fq(BBR 的 pacing 节奏依赖 fq),再把拥塞控制切到 bbr。没有 fq,BBR 会回退到 pfifo_fast,性能大打折扣。为什么必须配 fq,这是理解 BBR 最容易踩的坑。
七、缓冲区膨胀 bufferbloat:越大不一定越快
一个反直觉的陷阱是 bufferbloat(缓冲区膨胀)。当接收 / 发送缓冲区被设得过大,且中间路由器的队列也很大时,数据包会在队列里排队很久才被发出,导致延迟悄悄升高。你以为"缓冲大等于吞吐高",实际上应用层感受到的延迟(p50、p99)反而变糟,视频通话卡顿、SSH 打字发飘。这就是为什么红帽文档警告:最大值按 BDP 的 2 到 3 倍设即可,把"默认"档抬到 512 KiB 以上就要谨慎。
至此,原理篇讲完了"为什么"。你已经知道 BDP 怎么算、窗口为什么能缩放、自动调优怎么工作、TIME_WAIT 状态机是什么、BBR 和 CUBIC 的根本差异,以及 bufferbloat 的来由。带着这些理解去看下一篇实操模板,你会明白每份配置里每一个数字对应的痛点,而不是无脑复制。可直接照抄的 /etc/sysctl.d 模板、每条参数的生效命令、以及压测与失效排查方法,详见本系列第 02 篇:/vps-sysctl-tuning-guide-2026。