连接表被占满时,服务器最直接的表现是新连接无法建立,已有连接开始变慢甚至中断,服务整体处于“半死不活”的状态。
连接表是服务器维持网络通信的“通讯录”,一旦这个“通讯录”写满,系统就无法记录新的通信对象,很多运维新手遇到这种情况,第一反应是带宽不够或CPU满载,其实问题根源往往藏在内核参数里。
连接表是如何被占满的
每台服务器的连接表容量由内核参数决定,最常见的两个限制是net.ipv4.tcp_max_syn_backlog和net.core.somaxconn,前者控制半连接队列长度,后者控制全连接队列长度,两者共同决定了服务器能“多少正在握手的连接。
所谓“占满”,本质上就是这两个队列或者系统级连接跟踪表达到上限,三种典型场景最容易触发这种情况:
- 短连接高并发场景,大量连接快速建立又快速关闭
- 被DDoS攻击或恶意爬虫扫端口
- 应用层代码有bug,连接不主动关闭,导致CLOSE_WAIT状态堆积
当连接表快满时,系统日志里会频繁出现“possible SYN flooding”的告警,同时dmesg会输出socket: too many open files之类的报错信息。
连接表满了之后内核做了什么
内核面对满表的情况,策略非常“冷酷”,它不会通知任何应用层程序,而是直接丢弃新到的SYN请求包,更麻烦的是,如果开启了tcp_abort_on_overflow参数,内核会直接向客户端发送RST包,强制断开连接。
对客户端来说,表现就是:
- 连接请求长时间无响应,直到超时
- 直接被拒,报“Connection reset by peer”
- 反复重试仍无法建立连接
行业共识认为,这种“粗暴”处理方式虽然对老连接影响最小,但对业务可用性打击是致命的。
服务器表现出的具体“症状”
新连接大量失败,老连接游走在崩溃边缘
最典型的现象是新连接进不来,老连接出问题,已建立的连接看似正常,但一旦有数据传输需求,就会因为内核资源被挤占而出现RTO(重传超时)增加、响应变慢,如果有数据库操作,表现为连接池中的连接被执行时间突然变长,随后大量报错。

CPU不高,但应用响应奇慢
连接表占满时,CPU和内存占用往往并没有明显飙升,但应用的响应时间可能从几十毫秒飙升到几秒,原因是应用线程都在等待获取新的socket连接,线程处于阻塞状态,整个调用链被“卡”住,如果此时你通过top查看,会发现系统负载不高,但业务监控图上错误率已经拉满。
的时间等待队列暴涨
在ss -s的输出里,能直观看到TCP套接字数量,处于SYN_RECV、CLOSE_WAIT、TIME_WAIT状态的连接数量异常庞大,其中CLOSE_WAIT暴涨通常意味着应用代码没有正确关闭连接,而TIME_WAIT多一般是短连接场景的正常现象,但二者同时失控时,连接表必然告急。
本地端口耗尽引发连环故障
连接表被占满,往往意味着本地端口也被“消耗殆尽”,服务器作为客户端去请求下游服务时,内核无法分配新的本地端口,日志里会出现Cannot assign requested address,这个现象非常迷惑人,因为表面看起来像是网络不通,实际却是端口资源枯竭。
日志和监控里的异常信号
通过几个命令能快速确认连接表是否满了:
netstat -s | grep -i "SYNs to LISTEN"
这个命令显示丢弃的SYN包数量,如果数值在涨,说明半连接队列溢出。
ss -lnt
这个命令查看监听端口的队列情况,Recv-Q列如果长期等于Send-Q的设定值,说明全连接队列已满。
cat /proc/sys/net/ipv4/tcp_abort_on_overflow
返回1表示内核正在发送RST强行断开新连接。
监控工具方面,node_exporter提供了node_sockstat_TCP_alloc和node_netstat_Tcp_ListenOverflows指标,后者专门统计监听队列溢出次数,Zabbix则可以用net.tcp.listen[port]配合自定义脚本检测端口可用性,如果内部监控面板上突然出现这两个指标的持续走高,基本就能锁定问题。
实战排查三步走
用一个实际场景来说明排查链路,假设一台Nginx服务器突然大量报502,应用层检查后没有发现异常,按照以下步骤操作:
第一步,查看当前连接状态分布。

ss -ant | awk '{print $1}' | sort | uniq -c
如果发现SYN_RECV数量超过几百,且持续不下降,说明半连接队列溢出,攻击或者扫描的可能性较大。
第二步,确认是否存在端口耗尽。
cat /proc/sys/net/ipv4/ip_local_port_range
netstat -ant | wc -l
查看当前TCP连接总数是否逼近ip_local_port_range的差值上限。
第三步,针对性调整内核参数。
sysctl -w net.core.somaxconn=1024
sysctl -w net.ipv4.tcp_max_syn_backlog=2048
sysctl -w net.ipv4.tcp_fin_timeout=30
调整后观察ss -lnt的队列积压情况,据统计,多数情况下调大这两个队列值能缓解问题,但根本解法还是优化业务代码释放连接。
常见误判:和服务器负载高有什么区别
连接表被占满时,最容易和普通的高负载混为一谈,区别核心在于资源瓶颈不同。
| 现象 | 连接表满 | CPU/内存满载 |
|---|---|---|
| CPU使用率 | 正常或偏低 | 持续100% |
| 内存占用 | 基本不变 | 明显攀升 |
| load average | 可能正常 | 明显超标 |
| 日志特征 | listen overflow | 慢查询、GC频繁 |
另一个容易混淆的是带宽跑满,带宽瓶颈下,TCP重传率飙升,但连接表自身是正常的,表现为所有连接都慢,但新的连接能建立。
如何提前预防连接表被占满
预防的核心思路是“不让连接堆积”和“能快速清理”。
在业务层面,连接要设置合理的超时时间,数据库连接池、HTTP客户端都要配置空闲回收策略,避免连接被“无限期”占用,应用日志里如果出现大量Connection reset,优先排查代码中的socket是否在finally块中正确关闭。
在系统层面,开启tcp_tw_reuse和tcp_tw_recycle(仅在内核3.6之前的版本建议开启recycle)能加速TIME_WAIT回收,但recycle在NAT环境下有缺陷会导致丢包,部署在云上时需要谨慎评估。
在架构层面,如果服务器承载的压力远超连接表容量,建议考虑负载均衡集群分流,或者使用连接池复用替代频繁创建新连接,比如在Java应用中使用

commons-pool2管理连接,Go语言使用database/sql自带的连接池配置。
比较实用的配置组合如下:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 8192
net.core.somaxconn = 4096
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_fin_timeout = 15
开启tcp_syncookies的最大好处是,即使半连接队列耗尽,内核也能通过SYN Cookie机制维持握手能力,代价是略微增加CPU开销,对大多数web服务来说,这笔交易完全值得。
不同的连接表满了什么现象
需要区分一个小场景:应用程序自身连接数达到上限(ulimit -n限制,即文件描述符耗尽)和内核连接表满,表现有细微差别,前者在日志里会直接看到Too many open files,应用会立刻报错,而网络层面本身是通的,后者表现为网络层异常,但文件描述符资源仍是空闲的。
判断方法很简单:
cat /proc/sys/fs/file-nr
如果输出中第一个数接近第二个数(已分配文件句柄数接近总上限),则是文件描述符不足;如果ss -s显示套接字数量逼近系统上限,而file-nr还很低,那问题就在连接表或者端口配置上。
连接表被占满服务器表现常见问题解答
连接表被占满后,重启服务能解决吗?
能解决一时,但根因不排除会反复出现,重启进程会释放该进程占用的所有socket,连接表瞬间腾出空间,但如果业务代码有连接泄漏,重启后一段时间又会再次占满。
连接表被占满和CLOSE_WAIT堆积有什么关系?
CLOSE_WAIT堆积是应用未调用close()关闭对端已断开的连接,导致连接长期驻留在表中,每个处于CLOSE_WAIT的socket都占用一条连接表记录,大量堆积就能占满整张表。
增大连接表容量是根本解法吗?
不是,调大somaxconn和tcp_max_syn_backlog只是把“蓄水池”挖大,如果进水速度持续大于排水速度,再大的池子也会满,根本解法是控制并发连接数量,让连接用完即走,配合超时回收机制。