连接数没满,应用却连不上数据库——TCP backlog溢出与somaxconn的隐藏限制

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

有一次半夜被电话叫醒,应用侧疯狂报"连接数据库超时"。

我第一反应是连接池打满了,赶紧连上去看Threads_connected------才300多。max_connections设的是1000。

连接数远没满,为什么连不上?

我盯着监控看了半天,Threads_running也不高,CPU、内存、磁盘全都正常。问题不在数据库内部。

后来在netstat -s | grep -i listen的输出里,看到了一个不断增长的数字:listen queue overflow。

问题找到了:不是MySQL的连接数满了,是TCP连接队列满了。

先把这条链路上的几个概念说清楚:

Threads_connected ------MySQL当前已完成握手并accept的连接总数。它只统计"进了门"的连接。

TCP backlog队列 ------三次握手完成、但MySQL还没执行accept的连接,会排在一个队列里等待。队列长度由MySQL的backlog参数控制。

somaxconn ------Linux内核参数(/proc/sys/net/core/somaxconn),限制单个监听套接字 的队列最大长度。MySQL的backlog只是"申请值",实际生效的是min(backlog, somaxconn)。

listen queue overflow ------当队列已满,新的SYN包被内核直接丢弃,客户端看到的就是"连接超时"。这个次数可以通过netstat -s查看。

一、连接建立的完整链路

一条TCP连接从建立到被MySQL使用,要经过几个阶段:

第一步:TCP三次握手。 客户端发SYN,服务端回SYN+ACK,客户端回ACK。握手完成后,连接进入accept队列,等待MySQL处理。

第二步:MySQL accept。 MySQL的主线程从accept队列取出连接,分配线程、初始化会话变量、验证权限。这一步完成后,连接才计入Threads_connected。

关键点 :第一步完成、第二步还没执行的连接,不在Threads_connected的统计里。它们占着accept队列的位置,但不计入"已连接数"。

这就是"连接数没满却连不上"的核心原因。你看的Threads_connected是300,但accept队列可能已经排了128个等待处理的连接。队列满了,第129个连接直接被丢弃。

二、backlog和somaxconn的配合关系

很多人以为调大MySQL的backlog就完事了。实际上,backlog只是一个"申请值",最终生效的是它和somaxconn的较小值。

bash 复制代码
# 查看当前somaxconn
cat /proc/sys/net/core/somaxconn
# 输出:128(很多Linux发行版的默认值)

如果MySQL的backlog设了512,但somaxconn只有128,实际生效的队列长度就是128。你以为调大了,其实没生效。

这个坑非常隐蔽。因为MySQL不会报错,SHOW VARIABLES LIKE 'backlog'也显示512,看起来一切正常。但内核层面只给了128个位置。

排查方法:

bash 复制代码
# 查看监听队列的溢出次数
netstat -s | grep -i listen
# 输出中关注 "listen queue of a socket overflowed" 的次数

# 查看当前监听队列状态
ss -lnt
# Send-Q列是队列上限,Recv-Q列是当前排队的连接数
# 如果Recv-Q持续接近Send-Q,说明队列快满了

ss -lnt的输出里,Send-Q表示队列上限,Recv-Q表示当前排队的连接数。如果Recv-Q持续接近Send-Q,说明队列压力很大,随时可能溢出。

三、哪些场景最容易触发

场景一:高并发短连接。 PHP应用、Serverless函数、无连接池的脚本------每次请求都新建连接、用完即关。连接建立和销毁的频率极高,accept队列的压力最大。

场景二:应用重启后的连接风暴。 多实例应用同时重启,每个实例的连

接池同时初始化,瞬间产生大量并发连接请求。这时候Threads_connected还没涨上来,但accept队列已经爆了。

场景三:慢accept。 如果MySQL主线程被其他任务阻塞(比如执行某些管理命令、或者系统负载极高),accept的速度变慢,队列积压加剧。

场景四:突发流量。 秒杀、大促开始瞬间,流量突增,连接请求在极短时间内集中涌入。

四、解决方案:内核参数 + 连接池 + 监控

第一步:调大somaxconn。

bash 复制代码
# 临时生效
sysctl -w net.core.somaxconn=1024

# 永久生效,写入/etc/sysctl.conf
net.core.somaxconn = 1024

第二步:确认MySQL的backlog不小于somaxconn。

bash 复制代码
SHOW VARIABLES LIKE 'backlog';

如果backlog小于somaxconn,调大backlog(需要重启MySQL或修改配置文件)。

第三步:连接池增加重试机制。

即使队列偶发溢出,应用侧的重试可以兜底。连接池配置合理的connectionTimeout和重试次数,避免单次连接失败直接抛错给用户。

第四步:减少短连接。

能用连接池就用连接池。短连接不仅给accept队列带来压力,还会增加MySQL的线程创建和销毁开销。

第五步:建立监控。

把netstat -s | grep -i listen的overflow次数纳入监控。这个数字持续增长,就是队列溢出的明确信号。同时监控ss -lnt的Recv-Q,作为队列压力的早期预警。

五、小结

"连接数没满却连不上数据库"这个问题,根因往往在TCP连接队列,不在MySQL内部。Threads_connected只统计已accept的连接,排队中的连接不计入。backlog的实际生效值受somaxconn限制,两者必须配合调整。排查的核心是netstat -s的overflow计数和ss -lnt的队列状态。高并发短连接场景最容易触发,连接池重试和减少短连接是应用侧的配合手段。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽......我们下次见~

相关推荐
yunlaodacom1 小时前
华为云国际站服务代理商:BMS数据库延迟突然升高?磁盘I/O、网络与CPU排查方法
网络·数据库·华为云
SelectDB技术团队1 小时前
Apache Doris 的时间旅行与快照能力:边界、替代路径与可落地操作
数据库·apache·数据湖·apache doris·数据库选型·多模湖仓
2601_961963381 小时前
电子合同检索与全文搜索怎么测、怎么选:六个指标 + 五家平台对比 + 八个自测问题
数据库·后端·python·django·智能合约
zhangjw342 小时前
1-3 PostgreSQL 架构模型:实例、数据库、Schema 模式、表空间完整概念
数据库·postgresql·架构
小羊没烦恼!2 小时前
C#综合揭秘——Entity Framework 并发处理详解
java·开发语言·前端·javascript·数据库·mysql·c#
Databend2 小时前
周年庆结束了,湖仓为什么还在高消费?
大数据·数据库·agent
笃行3502 小时前
数据集成软件里最贵的一行字,叫“用同步工具做增量同步“
数据库
吴声子夜歌2 小时前
Docker入门与实战——Etcd(高可用的键值数据库)
数据库·docker·etcd
flytalei3 小时前
从 Docker Compose 到 K3s:逐浪博客 MySQL 数据迁移实战与一致性验证
mysql·docker·容器