【Linux 网络调优】5 分钟掌握高并发场景的 8 个内核参数,吞吐量翻倍不是梦
我踩过的那些坑
先说个真事。去年有个客户,50 台服务器跑 API 网关,QPS 死活压在 2w 上不去,CPU 才 40%。查了半天,最后发现是 net.core.somaxconn 还是默认的 128。改了之后直接冲到 6w+。
老实说,Linux 默认内核参数是为“兼容一切”设计的,不是为“高性能”设计的。你需要自己动手把它喂饱。
⚠️ 重要提醒:下面所有配置,先在测试环境验证,再用 sysctl -p 或重启确认生效。别一股脑全贴上去,根据你的场景选。
一、快速诊断:你的 Linux 网络现在有多烂?
动手调优之前,先看看当前状态。不分析就乱调,等于闭着眼给病人开药。
1.1 看丢包
# 看一眼网卡层面的丢包
ip -s link show eth0 | grep -A5 "RX:"
# 再看 TCP 层的重传和丢包
netstat -s | grep -E "segments retransmited|lost"
如果 RX dropped 一直在涨,说明网卡驱动队列不够深或者 CPU 处理不过来。如果重传率超过 1-2%,网络质量或拥塞控制可能有问题。
1.2 看连接队列
# 查看当前 TCP 连接状态分布
ss -s
# 看 SYN 队列溢出(半连接)
netstat -s | grep "SYNs to LISTEN"
SYNs to LISTEN 数字很大,说明 net.ipv4.tcp_max_syn_backlog 太小,连接请求被直接丢掉。
1.3 看软中断分布
# 看看软中断跑在哪几个核上,有没有某个核特别高
cat /proc/softirqs | head -1 && cat /proc/softirqs | grep NET_RX
如果某个 CPU 核的 NET_RX 数值是其他核的好几倍,说明中断绑得不均衡。这个问题等下在第四节解决。
1.4 看看现在的内核参数长啥样
sysctl -a | grep -E "net.core|net.ipv4.tcp|net.ipv4.ip_local"
很多默认值低得离谱——net.core.somaxconn 默认 128,net.ipv4.ip_local_port_range 起始端口 32768,能用的端口才 2 万多个。难怪高并发下扛不住。
二、核心参数调优:8 条让服务器起飞的神配置
直接上代码,创建 /etc/sysctl.d/99-network-performance.conf:
# /etc/sysctl.d/99-network-performance.conf
# 适用于:高并发 Web 服务 / API 网关 / 消息队列
# 1. TCP 监听队列(重要!!!)
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# 2. 网卡收包队列 - 防止突发流量丢包
net.core.netdev_max_backlog = 65536
# 3. TCP 缓冲区(最小、默认、最大)
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# 4. 本地端口范围 - 扩大可用端口池
net.ipv4.ip_local_port_range = 1024 65535
# 5. TIME_WAIT 复用(高并发短连接神器)
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
# 6. 开启 TCP 窗口缩放(高延迟链路必备)
net.ipv4.tcp_window_scaling = 1
# 7. SYN Cookie 防护(防攻击,也防队列满丢包)
net.ipv4.tcp_syncookies = 1
# 8. 启用 BBR(看下一节)
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
保存后加载:
sudo sysctl -p /etc/sysctl.d/99-network-performance.conf
# 或者直接加载所有
sudo sysctl --system
来逐条解释为什么是这些值:
somaxconn和tcp_max_syn_backlog:一个管全连接队列,一个管半连接队列。高并发下默认 128 根本不够,连接请求直接丢。改到 65535 是常规操作。netdev_max_backlog:内核从网卡收包后先丢进这个队列。如果你的服务偶发丢包但 CPU 又不高,十有八九是这个值太小。tcp_rmem/wmem:TCP 滑动窗口的缓冲区。第三个数字是上限,我习惯设 16MB,内存够的话可以更大(比如 64MB)。有个公式可以参考:BDP = 带宽(Mbps) × RTT(ms) / 8。比如 1Gbps 链路、10ms RTT,BDP ≈ 1.25MB,所以 16MB 绰绰有余。ip_local_port_range:改成 1024 到 65535,可用端口从 2.8 万变成 6.4 万。如果你的服务做大量主动出向连接(比如数据库连接池),这个很关键。tcp_tw_reuse:允许复用 TIME_WAIT 状态的端口。注意是reuse不是recycle——tcp_tw_recycle在 4.12+ 内核里已经被移除了,别碰。
三、BBR 拥塞控制:高延迟网络的救星
Google 开发的 BBR 算法通过估算带宽和 RTT 来动态调速,在高延迟高丢包场景下比传统的 Cubic 强太多了。我在一个跨国业务上实测,伦敦到新加坡的链路,吞吐量从 30Mbps 直接拉到 180Mbps。
启用 BBR 需要两个条件:
- 内核版本 ≥ 4.9
- 队列规则用
fq(公平队列)
# 检查内核版本
uname -r
# 启用 BBR(永久配置已在上一节写好,这里临时验证)
echo "bbr" > /proc/sys/net/ipv4/tcp_congestion_control
echo "fq" > /proc/sys/net/core/default_qdisc
# 验证是否生效
sysctl net.ipv4.tcp_congestion_control
# 预期输出: net.ipv4.tcp_congestion_control = bbr
一个坑必须提醒你:BBR 配上 fq 调度器才有效。如果你只改 BBR 不改 qdisc,TCP 栈会用自己的高精度定时器,CPU 消耗会明显增加。
另外,如果你跑的是纯内网服务(低延迟、高带宽),BBR 不一定比 Cubic 好。内网建议老老实实用 Cubic。
四、网卡层优化:别让硬件拖后腿
软件参数改完了,别忘了网卡本身也可能是个瓶颈。
4.1 先看看网卡队列够不够
# 查看当前队列数
ethtool -l eth0
输出类似这样:
Pre-set maximums:
RX: 8
TX: 8
Current hardware settings:
RX: 4
TX: 4
如果 Current 小于 Maximum,说明队列没用满。
4.2 增加队列数
# 把 RX 和 TX 队列都设成 8(根据你的 CPU 核数来,不要超过核数)
ethtool -L eth0 combined 8
队列数一般建议等于 CPU 核数或者核数的一半。比如 16 核机器可以设 8 或 16。
4.3 增大 Ring Buffer
# 查看当前 ring 大小
ethtool -g eth0
# 增大 RX 和 TX ring(具体数字看网卡支持)
ethtool -G eth0 rx 4096 tx 4096
Ring Buffer 越大,能扛的瞬时突发流量越强,但延迟也会增加。我测试过从 256 升到 4096,丢包明显减少,但 P99 延迟从 232µs 涨到了 484µs。这有个取舍,根据自己的业务决定。
4.4 中断绑定(进阶,别偷懒)
增加队列后,还要把每个队列的中断绑到不同的 CPU 核上,才能真正发挥多核性能。
# 查看每个队列的中断号
cat /proc/interrupts | grep eth0
输出类似:
96: 12345678 0 0 0 0 IR-PCI-MSI 327680-edge eth0-rx-0
97: 0 12345678 0 0 0 IR-PCI-MSI 327681-edge eth0-rx-1
...
把中断 96 绑到 CPU 0,中断 97 绑到 CPU 1,以此类推:
echo 1 > /proc/irq/96/smp_affinity # CPU 0 的掩码是 1
echo 2 > /proc/irq/97/smp_affinity # CPU 1 的掩码是 2
# CPU 掩码:CPU0=1, CPU1=2, CPU2=4, CPU3=8...
(顺便提一嘴,有些发行版用 irqbalance 自动做这个事情,但它不一定聪明。高性能场景我建议手动绑。)
五、验证与监控:调完怎么知道有效?
改完参数重启服务跑压测,但怎么确认改对了?
5.1 检查参数是否真的生效
# 逐个核对关键参数
sysctl net.core.somaxconn net.core.netdev_max_backlog net.ipv4.tcp_tw_reuse
5.2 用 ss 看连接队列状态
# 查看某个端口的监听队列状态
ss -lnt 'sport = :8080'
关注 Recv-Q 和 Send-Q。如果 Recv-Q 长期接近或等于 Send-Q,说明队列快满了,还需要调大 somaxconn。
5.3 用压测工具验证
# 安装 wrk
apt-get install wrk # 或 yum install wrk
# 跑个 30 秒的压测
wrk -t12 -c400 -d30s http://你的服务地址
观察 QPS 和延迟。对比调优前后的数据。
5.4 彩蛋:用 bpftrace 实时追踪 TCP 丢包
这是真·高阶技巧。如果你的丢包问题很隐蔽,传统工具看不出来,可以上 bpftrace。
# 安装 bpftrace(Ubuntu/Debian)
apt-get install bpftrace
# 追踪内核中 TCP 丢包事件
bpftrace -e 'kprobe:tcp_drop { printf("TCP drop: %s\n", kstack()); }'
运行这个命令后,每次内核丢掉 TCP 包都会打印内核调用栈,精准定位丢包发生在哪一层。
六、常见问题与踩坑记录
Q1:改了 tcp_tw_reuse=1,端口还是不够用?
A:检查你是不是还在用 tcp_tw_recycle——这个参数在 Linux 4.12+ 内核里已经被移除了,配置了也不报错但无效。另外确认你的应用是否真的绑了 SO_REUSEADDR。
Q2:改完 BBR 后 CPU 使用率反而升高了?
A:八成是忘了改 default_qdisc。检查一下:
sysctl net.core.default_qdisc
如果不是 fq,执行 sysctl -w net.core.default_qdisc=fq,然后重启服务。
Q3:ethtool -L 提示“Operation not supported”?
A:你的网卡驱动不支持动态调整队列数。试试看是不是在云虚拟机里——很多云厂商的虚拟网卡不支持这个操作。用 ethtool -i eth0 看看驱动信息。
Q4:改了参数后服务启动变慢?
A:检查 net.ipv4.tcp_max_syn_backlog 是不是设得太大了。如果内存不够,过大的 SYN 队列会占用资源。另外确认你的应用 listen 时传的 backlog 参数和内核的 somaxconn 取的是最小值,不是你设 65535 就一定能用 65535。
Q5:我的容器(Kubernetes/Docker)怎么改这些参数?
A:Docker 可以用 --sysctl 参数:
docker run --sysctl net.core.somaxconn=65535 my-image
Kubernetes 需要在 Pod 的 securityContext 里配置:
securityContext:
sysctls:
- name: net.core.somaxconn
value: "65535"
注意不是所有 sysctl 参数都允许在容器里改,有些需要在宿主机层面配置。
总结与互动
核心要点回顾:
- 先诊断再动手——用
ip -s link、netstat -s、ss -s定位瓶颈 - 8 个关键内核参数——
somaxconn、netdev_max_backlog、tcp_rmem/wmem、tcp_tw_reuse等,别再用默认值了 - 高延迟网络无脑上 BBR——但要配上
fq调度器 - 网卡层别忽略——
ethtool -L增加队列、ethtool -G调大 Ring Buffer、手动绑中断 - 验证不能省——压测 + 监控,数据说话
个人不推荐盲目抄网上的“一键优化脚本”。每个业务的流量模型不一样——API 网关和文件传输服务器需要优化的方向完全不同。先把我的配置在测试环境跑一遍,对比你的实际场景再微调。
最后抛个问题:你的线上服务有没有因为网络参数没调好出过事故?或者你遇到过什么奇怪的网络性能问题,折腾了好几天才解决?评论区聊聊,说不定你的解法能帮到更多人。
更多推荐

所有评论(0)