这不是连接数真不够,而是连接没被释放,因为应用层未正确关闭连接导致大量sleep连接堆积,且wait_timeout设置过大(如8小时),使空闲连接长期不释放,占满max_connections限额。

这不是连接数真不够,而是连接没被释放
为什么SHOW PROCESSLIST里全是Sleep状态连接
MySQL报Too many connections时,90% 的情况不是并发高,而是大量连接卡在Sleep状态不退出。这些连接通常来自应用层未正确关闭数据库连接,比如 Java 里忘了调conn.close()、Python 脚本用完cursor没close()、PHP 使用mysql_connect()后没mysql_close()。
常见表现:
-
SHOW PROCESSLIST中超过 80% 的连接Command是Sleep,且Time列持续增长(比如 > 300 秒) -
SHOW STATUS LIKE 'Threads_connected'接近max_connections,但Threads_running只有个位数 - 同一
USER+HOST组合反复出现几十甚至上百条记录
根本原因不是 MySQL 拒绝新连接,而是旧连接“赖着不走”,把坑占满了。
wait_timeout 和 interactive_timeout 设得太大
这两个参数控制空闲连接存活时间,默认值通常是 28800 秒(8 小时)。这意味着一个连接执行完查询后,只要不主动断开,就会在 Sleep 状态挂满 8 小时才被 MySQL 自动回收——而应用层早就不需要它了。
实操建议:
- 先查当前值:
SHOW VARIABLES LIKE '%timeout%'; - 临时收紧(重启失效):
SET GLOBAL wait_timeout = 60;和SET GLOBAL interactive_timeout = 60; - 永久生效:在
/etc/my.cnf的[mysqld]段下加两行,然后重启mysqld - 注意:
wait_timeout对非交互式连接(如应用连接池)生效;interactive_timeout主要影响mysql客户端这类交互式会话
调太小(如 10 秒)可能导致健康检查或短时重试失败,60–180 秒是较稳妥的起点。
应用连接池配置和 MySQL 参数不匹配
连接池(如 HikariCP、Druid)的 maximumPoolSize 必须小于 MySQL 的 max_connections,否则多个服务一起上就直接打满。更关键的是:连接池的空闲连接驱逐时间(如 idleTimeout)必须 短于 MySQL 的 wait_timeout,否则池子里的空闲连接会被 MySQL 先断掉,造成连接泄漏假象。
典型错配:
- HikariCP 的
idleTimeout=300000(5 分钟),但 MySQL 的wait_timeout=60(1 分钟)→ 连接被 MySQL 断开后,HikariCP 还以为它活着,下次用时报异常 - Druid 的
minIdle=20,但 MySQLmax_connections=151→ 单个服务就占掉七分之一,三个微服务就爆了 - 没设
leak-detection-threshold,连接泄漏发生后毫无感知
建议:连接池 maxActive 或 maximumPoolSize ≤ max_connections × 0.7,并确保 idleTimeout 。
连都连不上时怎么救急
当 mysql -u root -p 都报 Too many connections,说明连管理通道都被堵死了。这时候不能靠 SQL 命令,得从系统层入手:
- 优先用 socket 直连:
mysql -S /tmp/mysql.sock -u root -p(绕过 TCP 连接限制) - 如果 socket 也不行,确认是否启用了 MySQL 8.0.14+ 的管理员端口:
SELECT @@admin_address;,再用mysql -h127.0.0.1 -P<port> -u<admin_user> -p</admin_user></port>登录 - 实在无法登录,用
ps aux | grep mysqld找主进程 PID,发kill -TERM $PID安全关闭(别用kill -9) - 云数据库(如阿里云 RDS)不支持
KILL,必须走控制台“终止会话”或提工单
真正难处理的从来不是参数调多大,而是 Sleep 连接背后那个没关连接的 finally 块、那个漏写 close() 的循环、或者那个把 wait_timeout 当成“连接保活心跳间隔”的误解。











