如何解决MySQL连接时遇到Too many connections错误?

作者:袖梨 2026-07-12
先确认是否真连满:用root本地socket登录后执行三句——SHOW VARIABLES LIKE 'max_connections'; SHOW STATUS LIKE 'Threads_connected'; SHOW STATUS LIKE 'Max_used_connections'; 若Threads_connected接近上限但Threads_running极低,说明是Sleep连接堆积。

怎么确认是不是真连满了

别急着改配置。哪怕应用连不上,用 root 通过本地 socket 还能进——MySQL 预留一个紧急连接位,专留给有 SUPER 权限的管理员。

登录后立刻执行三句:

  • SHOW VARIABLES LIKE 'max_connections';——看上限,默认常是 151
  • SHOW STATUS LIKE 'Threads_connected';——当前已建连数
  • SHOW STATUS LIKE 'Max_used_connections';——历史峰值,比瞬时值更可靠

如果 Threads_connected 接近 max_connections,但 Threads_running 极低(比如 500 连接里只有 2 个在跑),说明全是 Sleep 状态,基本就是泄漏或空闲连接堆积。

为什么 SET GLOBAL max_connections 经常不生效

执行 SET GLOBAL max_connections = 1000; 后查变量还是旧值?卡点不在 MySQL 本身,而在三道硬限制:

  • 运行 ulimit -n,若返回 1024,而你想设 2000,MySQL 启动时会自动向下取整
  • systemd 管理的服务必须在 /usr/lib/systemd/system/mysqld.service[Service] 段加:LimitNOFILE=65536LimitNPROC=65536,再执行 systemctl --system daemon-reload && systemctl restart mysqld
  • MySQL 8.0.22+ 支持 SET PERSIST max_connections = 1000;,它写入 mysqld-auto.cnf,优先级高于 my.cnf——若已用此方式,改配置文件也不覆盖

连接池 idleTimeout 和 wait_timeout 必须匹配

多数线上事故根子在应用侧。比如 HikariCP 或 Druid 常见错配:

  • max-active(Druid)或 maximumPoolSize(HikariCP)设得比 max_connections 还高,多个服务一起压就爆
  • wait_timeout 设为 300 秒,但连接池的 idleTimeout 设成 600 秒,连接永远归还不回去
  • HikariCP 的 connection-timeout 默认 30 秒,网络抖动易堆积;建议调到 10–15 秒,并开 leak-detection-threshold

关键原则:idleTimeout 必须严格小于 wait_timeout,否则连接被 MySQL 断掉后,池子还当它活着,下次用就报错。

kill Sleep 连接只是应急,不是解法

SHOW PROCESSLIST; 里看到一堆 Sleep 连接,KILL 掉能抢出几个名额,但几小时后又满——因为泄漏还在继续。

重点查 USERHOST

  • SELECT USER, HOST, COUNT(*) FROM information_schema.PROCESSLIST GROUP BY USER, HOST; 定位异常 IP 或账号
  • 对确认无用的连接,用 KILL <code>ID; 逐个终止;不要用 KILL QUERY,它只停语句,连接还占着
  • 更稳妥的做法是调低超时:SET GLOBAL wait_timeout = 120;(非交互式连接 2 分钟自动断),SET GLOBAL interactive_timeout = 180;(交互式连接 3 分钟)

真正复杂的是那些没显式关连接的代码路径:PHP 脚本发生 fatal error 时,mysqlnd 不会自动归还连接,必须显式 mysqli_close();Java 里 try 块里忘了 finally { conn.close() },也一样堆满。

相关文章

精彩推荐