MySQL 连接数满排查实战:根因与解决方案

线上业务突然大面积报错 ERROR 1040 (HY000): Too many connections,新请求全被拒绝——这是 MySQL 连接数满 最典型的症状。本文从连接数模型讲起,给出临时止血、根因定位到连接池与 wait_timeout 根治的可落地实战命令,帮你把故障从”重启就好”变成”不再复发”。

一、现象与报错特征

连接数满时,应用侧通常表现为:新建连接直接抛 Too many connections;已建立连接还能勉强工作,但延迟陡增;监控上 Threads_connected 曲线顶到 max_connections 上限。它和”慢查询””死锁”不同,前者是”处理得慢”,而连接数满是”连进来都来不及”。

现象说明
应用报 1040 错误超过 max_connections,MySQL 拒绝新连接
监控 threads_connected ≈ max_connections连接池已耗尽,逼近上限
DB 本身 CPU 不高常见于”连接泄漏/池过大”,而非算力不足
重启后很快复发根因未解,流量一上来又打满

二、MySQL 连接数模型:先看懂这几个关键量

要治理连接数,必须分清”上限””当前””活跃”三件事。下面这组命令是排查的起点:

-- 允许的最大连接数(硬上限)
SHOW VARIABLES LIKE 'max_connections';

-- 当前已建立连接数
SHOW STATUS LIKE 'Threads_connected';

-- 正在执行查询的连接数(真正在干活)
SHOW STATUS LIKE 'Threads_running';

-- 非交互/交互连接的空闲回收时间
SHOW VARIABLES LIKE 'wait_timeout';
SHOW VARIABLES LIKE 'interactive_timeout';

简单说:Threads_connected 不能超过 max_connections;而 Threads_running 才是真实并发压力。如果 connected 很高、running 很低,说明大量连接是”闲着没释放”,问题在应用端而非数据库。

变量/状态含义调优方向
max_connections最大并发连接上限临时止血可上调,但受内存限制
Threads_connected当前连接总数逼近上限即告警
Threads_running活跃执行连接高则需优化慢查询
wait_timeout空闲连接回收秒数过长会堆积空闲连接

三、快速止血:临时调大 max_connections

故障期间先保业务,用 SET GLOBAL 临时放大上限(重启后失效,仅应急):

-- 临时把上限提到 500(按内存与业务估算)
SET GLOBAL max_connections = 500;

-- 改完立即确认生效
SHOW VARIABLES LIKE 'max_connections';

注意:每个连接都会占用 sort_buffer_size、read_buffer_size 等线程级内存,盲目调大会把实例撑到 OOM。若 MySQL 跑在 Docker 容器 中,容器的内存上限也会限制你能开多大,调之前先看 docker stats 与宿主机剩余内存。

四、根因定位:连接为什么降不下来

4.1 应用未释放连接(连接泄漏)

代码里拿到连接后没在 finally/try-with-resources 中关闭,连接一直挂到超时。这是最常见的”重启就好、过会儿又满”的真凶。

4.2 连接池配置过大

如 HikariCP 的 maximumPoolSize 设成 200,10 个服务实例就有 2000 个连接压向单库。池不是越大越好,应等于”单实例并发 × 实例数”的保守估计。

4.3 慢查询 / 长事务占满

一条没走索引的 SQL(详见 MySQL 索引底层与最左前缀)可能在 Threads_running 上挂很久,连接长期不释放,最终连带打满。

4.4 wait_timeout 过长

默认 28800 秒(8 小时)意味着一个忘了关的连接会占用半天。对 Web 应用,通常设 60~300 秒足够。

五、实战排查清单(含聚合命令)

用 information_schema.processlist 按来源 IP、用户、状态聚合,一眼看出是谁在”霸占”连接:

-- 按客户端 IP 聚合连接数,找"刷屏"的来源
SELECT SUBSTRING_INDEX(host,':',1) AS client_ip, COUNT(*) AS cnt
FROM information_schema.processlist
GROUP BY client_ip ORDER BY cnt DESC;

-- 按用户聚合
SELECT user, COUNT(*) AS cnt
FROM information_schema.processlist
GROUP BY user ORDER BY cnt DESC;

-- 按状态聚合,定位是 Sleep 堆积还是 Query 卡住
SELECT state, COUNT(*) AS cnt
FROM information_schema.processlist
GROUP BY state ORDER BY cnt DESC;

-- 揪出执行时间最长、可能拖垮连接的会话
SELECT id, user, host, db, time, state, info
FROM information_schema.processlist
WHERE command != 'Sleep' ORDER BY time DESC LIMIT 20;

若 Sleep 状态占了绝大多数,基本锁定”连接泄漏或 wait_timeout 过长”;若 Query 状态且 time 很大,则是慢 SQL/长事务。

processlist 状态指向的根因
大量 Sleep 且 time 很大连接泄漏 / wait_timeout 过长
大量 Query 且 time 很大慢查询 / 长事务
同 IP 连接数异常高某服务实例连接池过大
单一 user 占满该应用未正确释放连接

六、根治方案:从”调大”到”治本”

-- 把空闲回收时间收到合理范围(需写进配置文件持久化)
SET GLOBAL wait_timeout = 300;
SET GLOBAL interactive_timeout = 300;

-- 持久化示例(my.cnf / my.ini)
-- [mysqld]
-- max_connections = 500
-- wait_timeout = 300
-- interactive_timeout = 300

应用端则要:① 用连接池并配置 leakDetectionThreshold 主动发现泄漏;② 把 maximumPoolSize 调到与真实并发匹配;③ 给 SQL 加超时与熔断。架构上,对读多写少的场景可引入 合理的数据库设计规范 与读写分离,把连接压力分摊;上线前用 sysbench 压测 验证连接上限是否够用。

方案适用场景成本
调小 wait_timeout空闲连接堆积低,立即可做
修复连接泄漏代码未关闭连接中,需改代码
收敛连接池大小多实例池过大低,配置调整
ProxySQL 限流/复用微服务多客户端高,需引入中间件
读写分离读多写少中,架构改造

七、落地 Checklist(避坑清单)

步骤动作
1. 止血SET GLOBAL max_connections 临时上调,恢复写入
2. 看数对比 Threads_connected / running,判断是泄漏还是慢查询
3. 聚合按 IP/user/state 聚合 processlist,定位来源
4. 治本修泄漏 + 收池 + 降 wait_timeout + 加限流
5. 验证压测连接上限,监控 threads_connected 趋势

八、小结

MySQL 连接数满的本质,是”连接的产生速度超过了释放速度“。临时调大 max_connections 只是止血,真正的根治要从连接泄漏、连接池大小、wait_timeout 和架构分流四方面同时下手。把上面的 processlist 聚合命令存进监控面板的定时巡检,就能在打满之前提前预警。更多 MySQL 深度调优可参考 MySQL 深度调优实战。

九、容易被忽略的两个参数与监控预警

除了 max_connections,还有两个参数常在故障复盘时才被想起。其一是 max_user_connections,它按”用户”单独限连接数——当某个应用账号的连接把全局上限吃掉前,可能先触发该账号自己的上限,排查时要一起看;其二是 max_connect_errors,若某 IP 频繁握手失败(如错误的密码、网络抖动)超过阈值,MySQL 会直接拉黑该 host,报 Host 'x.x.x.x' is blocked,此时不是连接数满,却表现得像连不上,用 FLUSH HOSTS 即可解封。

-- 查看每个用户的上限(0 表示不限制,沿用全局)
SHOW VARIABLES LIKE 'max_user_connections';
SHOW VARIABLES LIKE 'max_connect_errors';

-- 某 host 被拉黑时解封
FLUSH HOSTS;

-- 观察线程缓存命中,判断是否频繁创建销毁连接线程
SHOW STATUS LIKE 'Threads_created';
SHOW STATUS LIKE 'Connections';

从架构层面,MySQL 是”一个连接对应一个线程”的模型,海量短连接会带来频繁的线程创建/销毁开销;引入线程池插件或前置连接复用层(如 ProxySQL)能显著降低这种抖动。最后别忘了把”连接数水位”做成监控:当 Threads_connected / max_connections 超过 70% 就告警,并配合应用侧限流(如网关令牌桶),把”事后救火”变成”事前拦截”。

上一篇 inotify 实战:实时文件监控与自动化响应
下一篇 Watchtower 实战:Docker 镜像自动更新
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]