【内核调优 02】sysctl 网络调优实操:可直接抄的模板与验证排错
2026-08-15 · DevCraft Studio
内核调优实操篇:测量基线后再动手,给出 tcp_rmem/somaxconn/TIME_WAIT/BBR/文件句柄的可直接抄 sysctl 模板、逐条生效与验证命令,以及常见坑位与失效排查清单。
延伸阅读
更多相关攻略推荐:【VPS 硬件选型指南 (CPU 篇) 04】AMD EPYC 还是、【发行版选型 07】Arch Linux 深度科普:滚动发布、pac、【VPS 进阶玩法精选 014】ARM 架构 VPS 值不值得上?A、【对象存储 01】对象存储怎么选:Backblaze B2 vs C、【TCP优化 01】美国 VPS 跑不快?多半是没开 BBR,一个命。
一、先别急着抄配置:测量你的基线
买便宜国外 VPS 的人,十个有八个都经历过这种场景:网站打开慢、接口响应飘、压测一上来连接就被拒绝。第一反应往往是"机器太垃圾了,换一家"。但真相是,绝大多数 Linux 发行版的内核网络参数,默认是为了省内存、保通用而设计的,根本不是给高并发 Web 服务准备的。调和不调,同样一台机器能扛的并发往往差出好几倍。不过在动任何参数之前,你得先分清:是配置型瓶颈(靠 sysctl 解决)还是资源型瓶颈(CPU 被超售、内存只有 512M,调破天也没用)。
调优最怕"凭感觉"。参数贴上去到底有没有用,要用数字说话。动手前先做三组测量,调完再测一次,前后对比才靠谱。吞吐用 iperf3 打流,连接建立速率用 wrk 或 ab 压本机接口,队列溢出用 netstat -s 看 listen 队列溢出计数——这是并发上不去最直接的证据。
sysctl net.core.somaxconn
sysctl net.ipv4.tcp_rmem
sysctl net.core.rmem_max
sysctl net.ipv4.tcp_congestion_control
ss -lnt | grep -c SYN-RECV
netstat -s | grep "listen"
先把当前值记下来,后面每个模板数字都对应一个具体痛点。参数背后的数学推导与 TCP 状态机原理,详见本系列第 01 篇:/sysctl-network-tuning-vps-2026。
二、TCP 读写缓冲区模板:tcp_rmem 与 tcp_wmem
这是最立竿见影的一组。Linux 默认给每个 TCP socket 的读写缓冲区只有一百多 KB,在跨国高延迟链路上,这点窗口根本喂不满带宽——你买的是 1 Gbps 端口,实际只能跑出一两百 Mbps,瓶颈就在这里。三个参数要配合着改:核心层上限 net.core.rmem_max / wmem_max,以及 TCP 层三档值 net.ipv4.tcp_rmem / tcp_wmem。注意 tcp_rmem 的"最大"突破不了 rmem_max 这个天花板,所以两个都得改。
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
把上限拉到 16 MB 左右,对大多数 1 GB 内存以上的 VPS 安全;内存紧张的小鸡降到 4 MB 到 8 MB,避免高连接数时把内存吃光。写完执行 sysctl -p 让内核读取,再用下面的命令确认新值已生效:
sysctl net.core.rmem_max
sysctl net.ipv4.tcp_rmem
三、连接队列模板:并发上不去的真正元凶
很多人压测时看到 connection refused,第一反应是应用写得烂,其实很可能是内核的 accept 队列满了。TCP 握手分两步队列:半连接队列(SYN_RECV,由 tcp_max_syn_backlog 管)和全连接队列(ESTABLISHED 但还没被应用 accept,由 somaxconn 管)。任何一个满了,新连接就被直接丢掉。Nginx、Caddy 设的 backlog 再大,也被 somaxconn 截断。
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 250000
改完用 ss -lnt 看 Recv-Q 和 Send-Q 两列。如果 Recv-Q 长期顶到 Send-Q 的值,说明队列还是满的,要么再加大,要么你的应用 accept 太慢——后者得去加 worker 或开连接复用,光调内核救不了。
ss -lnt
四、TIME_WAIT 与本地端口模板:短连接服务的隐形杀手
如果你的服务是大量短连接(频繁调外部 API、爬虫、代理),很快会遇到端口不够用。主动断开的一方进入 TIME_WAIT 状态,默认等两倍 MSL(约一分钟)才释放,几千个连接一打,本地端口瞬间被占满,新连接直接失败。
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.ip_local_port_range = 1024 65535
tcp_tw_reuse 在时间戳开启的前提下,安全复用 TIME_WAIT 端口给新出站连接,是解决端口耗尽最干净的办法;tcp_fin_timeout 把 FIN 等待从 60 秒砍到 15 秒;ip_local_port_range 把可用本地端口从默认两三千扩到六万多个。千万记住一条铁律:别碰 tcp_tw_recycle,它在 4.x 之后已废弃,在 NAT 环境下会导致连接大面积失败,只用 tcp_tw_reuse 就对了。
五、开启 BBR 与逐条验证
BBR 是 Google 出的拥塞控制算法,从内核 4.9 起内置。传统 CUBIC 在丢包时猛踩刹车,导致高延迟跨国链路吞吐暴跌;BBR 改为估算瓶颈带宽和 RTT,把速度拉满而不盲目退让。开启要两步:切拥塞控制到 bbr,并把队列规则设为 fq(BBR 的 pacing 需要 fq 配合)。再顺手开 TCP Fast Open,让客户端在握手第一个 SYN 包就带上数据,省掉一个 RTT。
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_fastopen = 3
生效后逐条验证,确认真的换过去了:
sysctl net.ipv4.tcp_congestion_control
sysctl net.ipv4.tcp_available_congestion_control
ss -tin | grep bbr
如果 available 列表里压根没有 bbr,说明内核太老,先升级内核(Ubuntu 用 HWE 内核,CentOS 7 走 ELRepo)再开。Docker 容器共享宿主机网络命名空间,BBR 必须在宿主机层面开,容器内改了不生效。
六、文件句柄与系统上限:too many open files 的根源
Web 服务器、数据库、反向代理,每个连接都吃一个文件描述符。Linux 默认单进程上限才 1024,超过就报 too many open files,连接建立直接失败。这要在两个层面一起改:系统全局 fs.file-max 给到二十万以上,单进程在 /etc/security/limits.conf 提 nofile,更关键的是在 systemd 服务单元加 LimitNOFILE,因为 limits.conf 经常管不到 systemd 拉起的服务。
fs.file-max = 2097152
/etc/security/limits.conf 加:
www-data soft nofile 65535
www-data hard nofile 65535
再给 nginx.service 的 [Service] 段加 LimitNOFILE=65535,然后 daemon-reload 重启。用 cat /proc/sys/fs/file-nr 看已用和上限,如果已用长期超过上限八成,就得继续往上加。
七、可直接抄的 /etc/sysctl.d 模板
把上面所有点揉成一份可直接落地的配置。建议写到 /etc/sysctl.d/99-tcp.conf 而不是直接改 /etc/sysctl.conf,这样以后好管理、好回滚。下面这份适合大多数 1 GB 内存以上、跑 Web 或 API 的 VPS:
# 拥塞控制
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
# TCP 缓冲区
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# 连接队列
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 250000
# TIME_WAIT 与端口
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.ip_local_port_range = 1024 65535
# 其他
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_timestamps = 1
fs.file-max = 2097152
保存后一条命令生效:
sysctl -p /etc/sysctl.d/99-tcp.conf
回滚方法很简单:把 /etc/sysctl.d/99-tcp.conf 整个删掉或改名备份,再执行 sysctl -p /etc/sysctl.d/99-tcp.conf,或者直接 reboot,内核就回到发行版默认值。因为全程没动内核、没动引导,回滚是秒级且无副作用的。建议改之前先 cp 一份原配置留底,出错随时还原。逐段验证比一次性全开更稳妥:每加一组参数就 sysctl -p 一次,并用 iperf3 或 wrk 观察数字变化,哪一段让延迟变高就单独回退那一段,避免一次性改完却分不清是哪个参数在作怪。
如果只改了 /etc/sysctl.conf,就跑 sysctl -p。注意:内存小于 1 GB 的小鸡,请把 rmem_max / wmem_max 和 tcp_rmem / tcp_wmem 的最大值都降到 4194304(4 MB)左右,否则高连接数时会把内存压垮,得不偿失。
八、常见坑位与失效排查
- 调了还是拒连接:先 ss -lnt 看 Recv-Q 是否顶满 Send-Q。满了说明应用 accept 跟不上,去加 worker 进程、开 keepalive,别迷信内核。
- 缓冲区开太大反而更慢:rmem_max 过大在长肥管道上会拉高 p50 延迟,用 curl -w 测 time_total,延迟涨了就往回收。
- BBR 没生效:确认内核 4.9+,确认宿主机(而非容器内)开启,确认 default_qdisc 是 fq。
- 软中断丢包:cat /proc/net/softnet_stat 第三列异常增长,说明网卡收包处理不过来,可适当调高 net.core.netdev_budget。
- 别开 tcp_tw_recycle:NAT 环境下会搞挂连接,只用 tcp_tw_reuse。
最后提醒一句:sysctl 只是把当前机器"该有的性能"榨出来,它变不出 CPU、加不了内存、也救不了超售严重的宿主机。如果基准测试显示内核已经很能打,业务流量依旧扛不住,那瓶颈在机器本身——这时候该做的是换不超售的大机器,而不是继续调参。每个数字背后的原理,可回看本系列第 01 篇:/sysctl-network-tuning-vps-2026。