先确认是否真连满:用root本地socket登录,执行SHOW VARIABLES LIKE 'max_connections'和SHOW STATUS LIKE 'Threads_connected';若后者≥前者−1且Threads_running很小,说明连接堆积在Sleep状态未释放,而非并发过高。

怎么确认是不是真连满了
别急着改配置,用 root 本地 socket 登进去(MySQL 会预留一个紧急连接位),立刻执行这两条命令:SHOW VARIABLES LIKE 'max_connections'; 看上限,默认是 151;SHOW STATUS LIKE 'Threads_connected'; 看当前连了多少。如果 Threads_connected 已经 ≥ max_connections − 1,且 Threads_running 很小(比如 2/151),说明几百个连接卡在 Sleep,不是并发高,是连接没释放。
为什么 SET GLOBAL max_connections 经常不生效
这条命令看着快,实际常被三道墙挡住:
- Linux 系统级文件描述符限制:执行
ulimit -n,若返回值是 1024,你设max_connections = 2000就会被 MySQL 启动时自动截断 - 配置写错段落:必须放在
[mysqld]段里,写在[client]或全局位置无效 - MySQL 8.0.22+ 的
SET PERSIST会写入mysqld-auto.cnf,优先级高于my.cnf,改配置文件可能根本不起作用
连接池 idleTimeout 必须小于 wait_timeout
这是 90% 线上事故的根子。比如 HikariCP 的 idleTimeout 设成 30 分钟,而 MySQL 的 wait_timeout 是默认的 28800 秒(8 小时),连接被 MySQL 主动断开后,池子还拿着失效连接不放,下次取用就报错。正确做法是:
- 先查 MySQL 当前超时:
SHOW VARIABLES LIKE 'wait_timeout'; - HikariCP 中设
idleTimeout = 600000(10 分钟),必须严格小于wait_timeout - Druid 则要检查
minEvictableIdleTimeMillis和timeBetweenEvictionRunsMillis是否匹配
清理 Sleep 连接不能一刀切
别一上来就 KILL 所有 Sleep 连接——监控、ETL 工具、长轮询接口都靠它活着。安全做法是:
- 先定位异常来源:
SELECT USER, HOST, COUNT(*) FROM information_schema.PROCESSLIST GROUP BY USER, HOST ORDER BY COUNT(*) DESC; - 只清理空闲太久的连接:
SELECT ID, USER, HOST, TIME FROM information_schema.PROCESSLIST WHERE COMMAND = 'Sleep' AND TIME > 300;(>5 分钟才考虑) - 逐个
KILL,不要用; KILL QUERY,它不停连接,只停语句
真正卡死的从来不是 max_connections 数值本身,而是应用层连接泄漏 + 连接池与 MySQL 超时参数错配形成的僵死循环。











