直接调高max_connections不能解决连接耗尽问题,90%根源是连接泄漏与wait_timeout和连接池idletimeout错配;需先查threads_connected、max_used_connections确认是否真连满,再同步调整配置文件、systemd限制及用户级连接数,并确保idletimeout严格小于wait_timeout。

直接调高 max_connections 不能解决连接耗尽问题,反而可能触发 OOM killer 杀掉 mysqld 进程——90% 的线上连接耗尽,根源是连接泄漏 + wait_timeout 与连接池 idleTimeout 错配,不是数值本身太小。
先确认是不是真连满了
别一看到 ERROR 1040 (HY000): Too many connections 就改配置。用 root 本地 socket 登录(MySQL 会预留一个紧急连接位),立刻执行:
-
SHOW VARIABLES LIKE 'max_connections';—— 看上限,默认是 151 -
SHOW STATUS LIKE 'Threads_connected';—— 看当前已建连数 -
SHOW STATUS LIKE 'Max_used_connections';—— 看历史峰值,比当前值更重要
如果 Threads_connected 接近 max_connections 但 Threads_running 很小(比如 151 个连接里只有 2 个在跑查询),说明大量连接卡在 Sleep 状态没释放,不是并发高,是应用没关连接或连接池配置错误。
临时调参只救急,且有三道墙拦着
SET GLOBAL max_connections = 1000; 看似快,但实际常被以下三道墙挡住:
- Linux 系统级文件描述符限制:
ulimit -n若为 1024,MySQL 启动时会自动截断你设的 1000,甚至更低 - systemd 服务单元限制:CentOS 7+/Ubuntu 16.04+ 默认
LimitNOFILE=1024,必须手动在/usr/lib/systemd/system/mysqld.service的[Service]段加LimitNOFILE=10000 - MySQL 8.0.22+ 的
SET PERSIST会写入mysqld-auto.cnf,优先级高于my.cnf,改配置文件可能根本不起作用
它只适合半夜报警时顶一下,重启后就回退,不能当长期方案。
永久生效必须配齐三处:配置文件 + systemd + 用户级限制
只改 my.cnf 不够,必须同步做三件事:
- 在
/etc/my.cnf的[mysqld]段写死:max_connections = 300(取Max_used_connections和应用连接池总规模较大值,向上取整到 50 的倍数) - 同步改
/usr/lib/systemd/system/mysqld.service,在[Service]下加:LimitNOFILE=3500(≥max_connections + 200) - 对每个应用用户显式设置:
ALTER USER 'app_user'@'%' WITH MAX_USER_CONNECTIONS 8;—— 这是 MySQL 5.7 唯一对已有用户立即生效的方式,SET GLOBAL max_user_connections完全无效
改完必须执行:systemctl daemon-reload && systemctl restart mysqld,再验证:cat /proc/$(pidof mysqld)/limits | grep "Max open files" 和 SELECT User, Host, max_user_connections FROM mysql.user WHERE User = 'app_user';
真正卡死的从来不是数字,而是连接池与 wait_timeout 错配
最常被忽略的一点:HikariCP 的 idleTimeout 必须小于 MySQL 的 wait_timeout(默认 28800 秒)。否则连接被 MySQL 主动断开后,池子还拿着失效连接不放,下次取用就报错。
安全做法是:idleTimeout 设为 1800000(30 分钟),wait_timeout 设为 21600(6 小时),并确保应用代码中所有 Connection 都在 try-with-resources 或显式 close()。清理 Sleep 连接不能一刀切,得先查来源:SELECT USER, HOST, COUNT(*) FROM information_schema.PROCESSLIST GROUP BY USER, HOST ORDER BY COUNT(*) DESC;,再针对性 KILL 超过 5 分钟的空闲连接。











