MySQL 1040 Too many connections:连接泄漏定位与上限设定

来源:互联网 时间:2026-09-04

网站隔三差五报一句数据库连接失败,刷新两下又好了。这种时好时坏的毛病最烦人,因为它不像宕机那样干脆,你没法守在屏幕前等它出现。等到真去查的时候,现场早没了。

MySQL 从 5.7 起 max_connections 默认就是 151,8.0 也沿用了这个值。超过之后新连接直接被拒,报 ERROR 1040。看到这个错,第一反应通常是把上限调大。我劝你先别急,因为多数情况下这不是容量问题,是连接泄漏。

怎么区分?看三条状态。Max_used_connections 是启动以来的峰值,如果它离上限还差得远就报了 1040,说明是瞬时尖峰;Threads_connected 是当前连接数;Connection_errors_max_connections 是被拒绝过的次数,这个数字只要不是 0,就说明上限确实被碰到了。

真正能定性的是 processlist 里的状态分布。如果大部分连接都挂在 Sleep 上,那就是泄漏——程序拿了连接不还,连接闲着占内存,还把名额占满了。这种情况你把上限调到 1000 也没用,只是把崩的时间往后推,等内存吃干抹净,OOM 就来了。

每个连接都要吃掉一块内存,来自 sort_buffer、join_buffer、read_buffer、read_rnd_buffer、thread_stack 这几个按连接分配的缓冲区。按默认配置算,一个连接大约 2 到 4 MB。把上限从 151 提到 1000,光连接本身就要多备两三个 G,这笔账得先算清楚。

-- 连接数体检:先看状态,再决定要不要调上限

 

-- 1. 上限、峰值、当前值、被拒次数,四条一起看

SHOW VARIABLES LIKE 'max_connections';

SHOW STATUS LIKE 'Max_used_connections';

SHOW STATUS LIKE 'Threads_connected';

SHOW STATUS LIKE 'Connection_errors_max_connections';

 

-- 2. 连接都在干什么:Sleep 多就是泄漏,Query 多才是真忙

SELECT command, count(*) AS cnt,

       max(time) AS max_time_sec

FROM information_schema.processlist

GROUP BY command ORDER BY cnt DESC;

 

-- 3. Sleep 超过 60 秒的连接,是重点怀疑对象

SELECT id, user, host, db, time, state

FROM information_schema.processlist

WHERE command = 'Sleep' AND time > 60

ORDER BY time DESC LIMIT 20;

 

-- 4. 单个连接的理论内存开销(MB)

SELECT ROUND((

  @@read_buffer_size + @@read_rnd_buffer_size + @@sort_buffer_size

  + @@join_buffer_size + @@binlog_cache_size + @@thread_stack

) / 1024 / 1024, 2) AS per_connection_mb;

 

确认是泄漏之后,处置方向就明确了:让空闲连接早点释放。wait_timeout 管非交互连接、interactive_timeout 管交互连接,默认都是 28800 秒也就是 8 小时,对 Web 应用来说长得离谱。改成 300 秒,空闲五分钟的连接自动断开,名额就回来了。

但这是治标。真正的病根在程序里——查询完没关连接、异常分支漏了释放、异步任务里拿了连接没还。改 wait_timeout 只是让泄漏的速度赶不上回收的速度,程序该修还得修。顺便说一句,长连接不是不能用,但要配合连接池,让池来控制总量,而不是让每个请求自己去连。

真到了业务确实需要更多连接的那天,再按内存算上限:可用内存减去 InnoDB 缓冲池、减去系统开销,剩下的除以单连接内存。算出来多少就是多少,别填整数也别往上凑。改完记得同步检查操作系统的文件描述符限制,那个数不够的话 MySQL 起不来。

数据来源:MySQL 8.0 官方参考手册

相关文章

标签:

A5创业网 版权所有