通过调整TCP/IP sysctl参数优化Linux网络吞吐量
实用的Linux TCP sysctl调优指南,涵盖吞吐量、缓冲区、拥塞控制及安全测试方法。
通过调整TCP/IP sysctl参数优化Linux网络吞吐量
Linux网络吞吐量调优始于一个枯燥的问题:到底是什么拖慢了速度?一台在10 Gbps链路上最高只能达到300 Mbps的服务器,可能存在TCP窗口问题、磁盘问题、CPU中断问题、虚拟网卡配置不当、丢包问题,或者应用程序以小数据块发送数据。sysctl调优只能解决其中部分问题。
正因如此,我将TCP/IP sysctl变更视为受控实验,而非神奇的性能配方。从基准开始,每次修改一小部分设置,重新测试并做好记录。如果你从网上复制一大段调优配置到/etc/sysctl.conf,可能会改善某个工作负载,却悄悄损害另一个。
以下设置适用于运行高吞吐量服务的场景:制品仓库、备份服务器、对象存储网关、繁忙的反向代理、传输大量日志的数据库副本,或跨长距离链路传输流量的Linux主机。如果瓶颈在于TLS加密CPU、慢速存储、应用程序锁竞争、云服务商限制或主机外部的丢包,这些设置可能帮助不大。
在修改任何内容前,先收集基准数据:
ip -s link
ss -s
nstat -az | egrep 'TcpRetransSegs|TcpExtTCPLoss|TcpExtTCPTimeouts|TcpExtListenOverflows'
sar -n DEV,TCP,ETCP 1 10
iperf3 -c test-host -P 4 -t 30
如果看到重传率上升,先解决丢包问题再提高缓冲区。如果CPU在top、mpstat或perf中已经满载,sysctl可能掩盖症状但无法消除瓶颈。如果iperf3很快但应用很慢,先检查应用路径再调优内核。
sysctl在网络调优中的角色
sysctl允许在系统运行时暴露内核参数。网络设置通常位于net.ipv4、net.ipv6和net.core下。可以这样读取值:
sysctl net.ipv4.tcp_congestion_control
sysctl net.ipv4.tcp_rmem
sysctl net.core.rmem_max
可以这样测试临时变更:
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
临时变更重启后消失。持久化配置应放在专用文件中,例如/etc/sysctl.d/90-network-throughput.conf,而不是无说明地散落在/etc/sysctl.conf中。
sudo install -m 0644 /dev/null /etc/sysctl.d/90-network-throughput.conf
sudo editor /etc/sysctl.d/90-network-throughput.conf
sudo sysctl --system
使用单独文件的好处是回滚简单:移走文件并重新运行sudo sysctl --system。这在某个设置在生产流量下表现异常时至关重要。
TCP缓冲区:为长连接留出喘息空间
人们首先关注的是缓冲区大小。TCP需要足够的发送和接收窗口空间,以便在确认包穿越网络时保持数据在途。有用的思维模型是带宽延迟积:高带宽、高延迟的连接比低延迟的局域网连接需要更多在途数据。
例如,1 Gbps传输在1 ms延迟的数据中心链路上所需的在途数据远少于70 ms延迟的广域网链路。如果接收窗口太小,即使链路有空余,发送方也会暂停。
Linux使用三个值的数组进行TCP内存调优:
net.ipv4.tcp_rmem = 4096 131072 33554432
net.ipv4.tcp_wmem = 4096 131072 33554432
三个数字分别表示每个套接字缓冲区的最小值、默认值和最大值(字节)。具体值应与工作负载、内存预算和内核行为匹配。上面的示例将最大值提高到32 MiB,这对繁忙的服务器通常足够且不过分。某些长距离或存储密集型系统使用更大的值,但应通过真实流量测试。
net.core限制会限制套接字缓冲区:
net.core.rmem_max = 33554432
net.core.wmem_max = 33554432
如果tcp_rmem允许TCP增长到32 MiB但net.core.rmem_max低得多,实际生效的是较低的上限。除非有特殊原因,否则保持上限与TCP最大值一致。
不要在拥有大量并发连接的机器上盲目提高缓冲区。处理少量大流的文件服务器可以承受更大的每流缓冲区。处理数十万连接的代理如果让每个套接字都有资格使用巨大缓冲区,会迅速消耗内存。
自动调优已经在发挥作用
现代Linux内核已经自动调优TCP缓冲区。这意味着通常不需要在应用程序中设置巨大的固定套接字缓冲区。当连接需要更多空间时,内核会自动增长缓冲区。
你的主要工作是确保上限不会太低。如果在长肥网络(long fat network)上吞吐量不佳,且ss -tin显示接收窗口小或发送方被接收方阻塞,提高tcp_rmem、tcp_wmem、rmem_max和wmem_max会有帮助。
使用以下命令检查活动连接:
ss -tin dst <peer-ip>
关注cwnd、rtt、rto、bytes_acked、bytes_received和重传计数器等字段。它们比单次速度测试更能说明问题。
拥塞控制:CUBIC、BBR与现实
拥塞控制算法决定TCP如何增长或缩减发送速率。在许多Linux系统上,CUBIC是默认算法,适用于通用互联网和数据中心流量。BBR可以在某些有损或长距离路径上提高吞吐量和降低延迟,因为它基于瓶颈带宽和往返时间建模,而不仅仅对丢包做出反应。
检查可用算法:
sysctl net.ipv4.tcp_available_congestion_control
sysctl net.ipv4.tcp_congestion_control
仅当内核支持时才启用BBR:
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
持久化配置:
net.ipv4.tcp_congestion_control = bbr
某些系统还需要公平排队数据包调度器以实现良好的BBR行为:
net.core.default_qdisc = fq
不要假设BBR总是更快。它可能改变与其他流的公平性,不同内核版本中的BBR行为也不同。在你关心的流量模式上测试:大量小API调用、少量大文件传输、数据库复制流量或混合的生产级负载。
监听队列:在问题变成谜团前修复丢包
吞吐量问题有时表现为流量高峰期间的连接失败。如果服务接受新TCP连接的速度慢于客户端创建速度,内核队列会填满。
相关设置:
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 8192
somaxconn限制应用程序通过listen(2)请求的已完成连接积压队列。tcp_max_syn_backlog影响半开SYN队列容量。提高它们可以帮助繁忙的Web服务器、代理和负载均衡器,但应用程序也必须请求足够大的积压队列。Nginx、HAProxy、Envoy和应用服务器通常有自己的积压设置。
监控溢出情况:
nstat -az | egrep 'ListenOverflows|ListenDrops|Syncookies'
ss -ltn
如果ListenOverflows上升,说明内核队列跟不上。如果CPU饱和或应用被下游服务阻塞,增加队列大小可能暂时减少客户端错误,但无法修复服务本身。
积压与数据包处理
net.core.netdev_max_backlog控制当内核接收数据包速度快于处理速度时,输入队列中可以等待的数据包数量。
net.core.netdev_max_backlog = 250000
这有助于高速接口在突发流量时,特别是虚拟化网络环境中。但如果将主机变成大型数据包等待室,也可能增加延迟。首先检查接口丢包:
ip -s link show dev eth0
ethtool -S eth0 | egrep 'drop|err|timeout|miss|fifo'
如果驱动级别的丢包在上升,还需要检查NIC环形缓冲区大小、中断分布、RSS队列和CPU亲和性。这些超出了sysctl的范围,但在10 Gbps及更高速的主机上,它们通常比TCP缓冲区更重要。
TIME_WAIT与端口耗尽
高吞吐量的客户端、代理和作业运行器可能耗尽临时端口或积累大量处于TIME_WAIT状态的套接字。这里要小心,因为旧的调优建议可能有害。
检查当前范围:
sysctl net.ipv4.ip_local_port_range
ss -tan state time-wait | wc -l
合理的客户端调整是扩大临时端口范围:
net.ipv4.ip_local_port_range = 10240 60999
避免使用推荐tcp_tw_recycle的旧建议;该参数已从Linux中移除,因为它会破坏有效流量,尤其是在NAT后面。tcp_tw_reuse在许多内核中存在,但其行为随时间变化。不要将其作为默认吞吐量设置启用。如果认为需要它,请仔细测试确切的内核和流量模式。
对于服务器,大量TIME_WAIT套接字通常是正常的。对于客户端,端口耗尽通常意味着需要连接池、keep-alive、HTTP/2、减少短生命周期出站连接或增加源IP。
保守的起始配置文件
以下是高吞吐量服务器的实用起点。特意不过分极端:
# /etc/sysctl.d/90-network-throughput.conf
# 为高带宽或高延迟路径设置更大的TCP自动调优上限。
net.core.rmem_max = 33554432
net.core.wmem_max = 33554432
net.ipv4.tcp_rmem = 4096 131072 33554432
net.ipv4.tcp_wmem = 4096 131072 33554432
# 为突发入站流量设置更大的队列。
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 8192
net.core.netdev_max_backlog = 250000
# 可选:全局启用前先测试。
# net.core.default_qdisc = fq
# net.ipv4.tcp_congestion_control = bbr
应用配置:
sudo sysctl --system
然后重新测量。使用相同的测试大小、时间窗口、并行流数量和网络路径。一个改变了五个变量的前后对比测试不能作为证据。
常见错误
第一个错误是在有损路径上调优。TCP将丢包视为拥塞。更大的缓冲区可能略微提高吞吐量,但也可能增加延迟并掩盖真正的问题。先修复坏线缆、过载的虚拟交换机、数据包策略、MTU不匹配和不可靠的VPN路径。
第二个错误是假设iperf3 -P 8能证明应用程序性能。并行流可以填满链路,即使单个真实应用连接做不到。这是有用信息,但不是全部。
第三个错误是在共享主机上设置巨大的缓冲区。当内核仅在需要时增长缓冲区时,更大的上限没问题,但内存压力会改变一切。变更后监控free、slabtop、TCP内存和应用程序内存。
第四个错误是忘记回滚。在变更工单或运行手册中保留之前的值:
sysctl -a | egrep 'net.core.rmem_max|net.core.wmem_max|net.ipv4.tcp_rmem|net.ipv4.tcp_wmem|net.ipv4.tcp_congestion_control'
何时sysctl不是解决方案
如果某个CPU核心满载而其他核心空闲,检查中断处理、RSS、RPS/XPS和应用程序线程。如果磁盘等待高,网络可能在等待存储。如果TLS消耗CPU,测试有无加密的情况,并考虑硬件、密码套件选择或连接复用。如果Kubernetes或云负载均衡器位于路径中,检查服务级别限制和conntrack表。
对于NAT密集型主机,还要检查conntrack:
sysctl net.netfilter.nf_conntrack_count
sysctl net.netfilter.nf_conntrack_max
这不是TCP吞吐量调优,但conntrack耗尽可能表现为随机的网络缓慢或连接断开。
测试而不自欺
将iperf3作为网络工具使用,而不是证明应用程序已修复的证据。单流测试很有用,因为它显示单个TCP连接的能力:
iperf3 -c test-host -t 30
并行测试显示链路是否可以被多个流填满:
iperf3 -c test-host -P 8 -t 30
如果并行流很快但单流很慢,检查拥塞控制、TCP窗口增长、RTT和丢包。如果两者都慢,检查更底层:接口错误、云带宽限制、CPU饱和、MTU、防火墙检查或发送方和接收方背后的存储。
保持测试路径真实。在同一机架中测试两台主机无法告诉你跨区域的备份作业情况。使用小文件测试无法暴露稳态吞吐量。通过VPN测试可能更多地测量VPN设备而非Linux TCP。
每次变更后,捕获相同的计数器:
nstat -az > /tmp/nstat-after.txt
ss -s
sar -n DEV,TCP,ETCP 1 10
有用的结果不仅仅是“数字上升了”。你需要知道重传是否下降、队列是否停止溢出、CPU是否保持合理、小请求的延迟是否没有变差。
良好的Linux网络调优是可测量且可逆的。当路径需要更大的窗口时提高TCP缓冲区上限。测试拥塞控制而不是假设某种算法在所有场景都胜出。当看到队列丢包时增加监听队列,如果应用程序无法足够快接受则修复应用。sysctl很有用,但它只是更大系统中的一个层面。