🚨 前言:凌晨 3 点的报警电话
如果说有一个 MySQL 报错能让运维在半夜惊坐起,那一定是ERROR 1040: Too many connections。
当这个报错出现时,意味着数据库已经拒绝服务。你的后端应用连不上库,前端页面转圈圈,报警群里全是 500 错误。此时你试图用 Navicat 连上去排查,结果发现连你自己也挤不进去了。
很多人的第一反应是:“改大max_connections不就行了吗?”
错!大错特错!无脑调大参数,只会让服务器死得更透,甚至导致 OOM(内存溢出)。
今天,我就把这套**“教科书级”**的排查与修复方案分享出来,帮你彻底终结这个顽疾。
🔥 第一阶段:紧急止血(先让系统活过来)
当生产环境已经挂了,别谈架构优化,先恢复服务!
1. 利用预留通道登录
MySQL 默认会为拥有SUPER权限的用户(通常是 root)预留1 个连接名额。
即使普通连接数满了,你依然可以用命令行登录:
mysql -u root -p2. 杀掉空闲连接 (Sleep 线程)
进去后,执行SHOW PROCESSLIST,你会发现大量的Sleep状态的连接。它们占着茅坑不拉屎。
手动杀(太慢):
KILL1024;-- 1024 是线程 ID批量杀(救命脚本):
可以直接复制以下 SQL,生成批量 Kill 语句:
SELECTconcat('KILL ',id,';')FROMinformation_schema.processlistWHEREcommand='Sleep'ANDtime>60;-- 杀掉空闲超过60秒的执行生成的语句,连接数瞬间下降,业务恢复。
🔍 第二阶段:根因分析(为什么会满?)
连接数爆满,通常只有三种原因。我们需要用一张图来梳理连接的流向:
- 慢查询 (Slow SQL):这是最常见的原因。一条 SQL 跑了 10 秒,意味着这个连接被霸占了 10 秒。并发一高,连接池瞬间被慢 SQL 填满。
- 连接泄露 (Leak):代码里打开了连接却忘了
close(),导致连接一直处于Sleep状态,直到 8 小时后才断开。 - 配置不当:业务并发真的很高(比如 QPS 5000+),但 MySQL 默认的
max_connections只有151,确实不够用。
🛠️ 第三阶段:参数优化(治标)
在确认硬件资源充足的情况下,我们可以调整 MySQL 参数。
1. 调整最大连接数 (max_connections)
不要无脑设置 10000!每个连接都会消耗内存(线程栈、Buffer 等)。
推荐公式:可用内存 / 10MB(保守估计)。
例如 8G 内存的服务器,设置为1000 - 1500是比较安全的。
-- 临时生效SETGLOBALmax_connections=1000;-- 永久生效:请修改 my.cnf2. 缩短空闲等待时间 (wait_timeout)
MySQL 默认的wait_timeout是28800秒(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绝不是简单地把参数改大。
最佳实践路径:
- 救火:杀掉 Sleep 线程,优先恢复业务。
- 排查:开启慢查询日志,优化那些执行时间长的 SQL。
- 配置:将
wait_timeout调小至 600s,合理设置max_connections。 - 架构:检查连接池配置(HikariCP),引入 Redis 缓存。
记住:MySQL 的连接是昂贵的资源,用完请记得还。
博主留言:
你的生产环境max_connections设置的是多少?有没有遇到过因为设置太大导致 OOM 的情况?
在评论区回复“参数”,我发给你一份《MySQL 生产环境关键参数调优模板 (my.cnf)》,直接抄作业!