news 2026/4/18 10:03:46

完美解决 MySQL “Too many connections“ 报错:从参数优化到架构调整的全套方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
完美解决 MySQL “Too many connections“ 报错:从参数优化到架构调整的全套方案

🚨 前言:凌晨 3 点的报警电话

如果说有一个 MySQL 报错能让运维在半夜惊坐起,那一定是ERROR 1040: Too many connections

当这个报错出现时,意味着数据库已经拒绝服务。你的后端应用连不上库,前端页面转圈圈,报警群里全是 500 错误。此时你试图用 Navicat 连上去排查,结果发现连你自己也挤不进去了

很多人的第一反应是:“改大max_connections不就行了吗?”
错!大错特错!无脑调大参数,只会让服务器死得更透,甚至导致 OOM(内存溢出)。

今天,我就把这套**“教科书级”**的排查与修复方案分享出来,帮你彻底终结这个顽疾。


🔥 第一阶段:紧急止血(先让系统活过来)

当生产环境已经挂了,别谈架构优化,先恢复服务!

1. 利用预留通道登录

MySQL 默认会为拥有SUPER权限的用户(通常是 root)预留1 个连接名额。
即使普通连接数满了,你依然可以用命令行登录:

mysql -u root -p
2. 杀掉空闲连接 (Sleep 线程)

进去后,执行SHOW PROCESSLIST,你会发现大量的Sleep状态的连接。它们占着茅坑不拉屎。

手动杀(太慢):

KILL1024;-- 1024 是线程 ID

批量杀(救命脚本):
可以直接复制以下 SQL,生成批量 Kill 语句:

SELECTconcat('KILL ',id,';')FROMinformation_schema.processlistWHEREcommand='Sleep'ANDtime>60;-- 杀掉空闲超过60秒的

执行生成的语句,连接数瞬间下降,业务恢复。


🔍 第二阶段:根因分析(为什么会满?)

连接数爆满,通常只有三种原因。我们需要用一张图来梳理连接的流向:

正常获取
慢SQL阻塞
代码未关闭
缓存策略
客户端应用
连接池状态
MySQL Server
Active: 正在执行SQL
Sleep: 空闲连接
连接无法释放
连接数耗尽
连接泄露
正常空闲
  1. 慢查询 (Slow SQL):这是最常见的原因。一条 SQL 跑了 10 秒,意味着这个连接被霸占了 10 秒。并发一高,连接池瞬间被慢 SQL 填满。
  2. 连接泄露 (Leak):代码里打开了连接却忘了close(),导致连接一直处于Sleep状态,直到 8 小时后才断开。
  3. 配置不当:业务并发真的很高(比如 QPS 5000+),但 MySQL 默认的max_connections只有151,确实不够用。

🛠️ 第三阶段:参数优化(治标)

在确认硬件资源充足的情况下,我们可以调整 MySQL 参数。

1. 调整最大连接数 (max_connections)

不要无脑设置 10000!每个连接都会消耗内存(线程栈、Buffer 等)。
推荐公式:可用内存 / 10MB(保守估计)。
例如 8G 内存的服务器,设置为1000 - 1500是比较安全的。

-- 临时生效SETGLOBALmax_connections=1000;-- 永久生效:请修改 my.cnf
2. 缩短空闲等待时间 (wait_timeout)

MySQL 默认的wait_timeout28800秒(8小时)。这意味着一个连接空闲了 8 小时才会被回收,太浪费了!
推荐设置:300秒 - 600秒

-- 这里的单位是秒SETGLOBALwait_timeout=600;SETGLOBALinteractive_timeout=600;

这样,那些占着连接不干活的线程,10 分钟后就会被 MySQL 自动踢掉。


🏗️ 第四阶段:架构与代码调整(治本)

参数调整只是缓兵之计,真正的“完美解决”在应用层。

1. 使用高性能连接池 (HikariCP)

如果你还在用 C3P0 或 DBCP,请立刻换成HikariCP(Spring Boot 2.x 默认)。
并合理设置maximumPoolSize

注意:连接池不是越大越好!
推荐公式:CPU核心数 * 2 + 1
对于一个 4 核的 Web 服务器,连接池设置10-20就足够处理极高的并发了。设置成 1000 反而会因为线程上下文切换导致性能下降。

2. 引入缓存 (Redis)

80% 的查询请求应该被 Redis 拦截,只有 20% 的请求打到 MySQL。这是降低连接数最有效的手段。

3. 读写分离

主库负责写,从库负责读。将大量的查询流量分流到多个从库上,主库的连接压力自然就下来了。

4. 检查代码中的try-catch-finally

确保每一次数据库操作,都在finally块中释放了连接,或者使用 Java 7 的try-with-resources语法。

// 正确写法try(Connectionconn=dataSource.getConnection()){// 执行业务}// 出了大括号,连接自动归还给连接池

📝 总结

解决Too many connections绝不是简单地把参数改大。

最佳实践路径:

  1. 救火:杀掉 Sleep 线程,优先恢复业务。
  2. 排查:开启慢查询日志,优化那些执行时间长的 SQL。
  3. 配置:将wait_timeout调小至 600s,合理设置max_connections
  4. 架构:检查连接池配置(HikariCP),引入 Redis 缓存。

记住:MySQL 的连接是昂贵的资源,用完请记得还。


博主留言:
你的生产环境max_connections设置的是多少?有没有遇到过因为设置太大导致 OOM 的情况?
在评论区回复“参数”,我发给你一份《MySQL 生产环境关键参数调优模板 (my.cnf)》,直接抄作业!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!