根本原因是连接池未及时归还连接或MySQL未及时释放,导致大量Sleep连接堆积占用max_connections槽位;常见于idleTimeout > wait_timeout、事务未提交、maximumPoolSize超配及脚本不close连接等场景。

为什么连接池会触发Too many connections
根本原因不是连接池“用了太多连接”,而是它没及时归还、或归还后 MySQL 没立刻释放——连接卡在 Sleep 状态,持续占着 max_connections 槽位。常见场景包括:
- 应用层配置的
idleTimeout(如 HikariCP 的idle-timeout)大于 MySQL 的wait_timeout,导致连接池以为还能用,MySQL 却已悄悄断开,连接变成“假活”状态 - 事务未提交/回滚,
@Transactional方法抛异常但没 rollback,连接被长期持有 - 连接池设置了过高的
maximumPoolSize,多个服务实例叠加后远超 MySQL 实际承载能力(比如 5 个微服务各设 100,实际打到 DB 的就是 500+) - 脚本类任务(Python/PHP CLI)每次执行都新建连接,且不 close,跑完进程退出但连接未释放(尤其在容器里 PID 复用时更隐蔽)
如何确认是连接池导致的堆积
别只看 SHOW PROCESSLIST,重点查三组值是否匹配预期:
-
SHOW VARIABLES LIKE 'wait_timeout';—— MySQL 默认是 28800 秒(8 小时),Web 应用应调至300~600 -
SELECT @@wait_timeout;—— 确认当前会话生效值(有些客户端会覆盖全局) - 查连接池日志或监控:HikariCP 可配
leakDetectionThreshold=60000(毫秒),超时未归还会报 warn
如果 Threads_connected 长期 > Threads_running 且差值稳定在几十上百,基本就是连接池空闲连接没回收干净。
调整 max_connections 时权限和系统限制怎么绕过
SET GLOBAL max_connections = 2000; 常失败,不是权限不够,而是被三道墙拦住:
-
操作系统文件描述符限制:
ulimit -n返回值必须 ≥max_connections + 50(MySQL 自身也要开日志、socket 等)。systemd 服务需改/usr/lib/systemd/system/mysqld.service,加LimitNOFILE=10000 -
配置文件写错段落:必须放在
[mysqld]下,写在[client]或[mysql]段完全无效 - MySQL 8.0.22+ 的 SET PERSIST 优先级更高:若之前用过
SET PERSIST max_connections = 1500;,它会写进mysqld-auto.cnf,直接改my.cnf不起作用,得先RESET PERSIST max_connections;
真正该调的不是 max_connections,而是 wait_timeout
盲目把 max_connections 从 151 改成 1000,可能让服务器内存吃紧(每个连接约 1–2MB),而多数业务真实并发远低于此。更安全有效的做法是:
- 在
my.cnf的[mysqld]段加:wait_timeout = 300和interactive_timeout = 600 - 重启 MySQL 后验证:
SHOW VARIABLES LIKE '%timeout%'; - 同步改应用连接池:HikariCP 的
idle-timeout设为 240,max-lifetime设为 1800,确保一定比 MySQL 短
这个组合能快速清理掉挂死的 Sleep 连接,比硬扩上限管用得多——很多线上故障修复后发现,Threads_connected 从 498 直降到 32,根本不需要调 max_connections。


















