网站突然大量返回数据库连接失败,日志里出现Too many connections或错误码1040,最直觉的处理是增大max_connections。这个动作有时能暂时恢复请求,也可能把问题从“连接被拒绝”推成内存耗尽、上下文切换增加或数据库整体变慢。
连接数上限只是闸门,不是根因。先判断是谁占满连接、这些连接在做什么、应用连接池理论上能开多少,再决定调整MySQL、应用还是查询。
先保留一个管理入口
故障发生时不要在业务高峰反复重启MySQL。先确认是否还能通过本机管理账户进入;不同版本和权限配置对管理连接保留机制的支持不同,应提前建立受控的运维账户和访问路径,而不是出故障后临时开放远程高权限登录。
能够进入数据库后,先收集快照:
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Threads_running';
SHOW GLOBAL STATUS LIKE 'Max_used_connections';
SHOW GLOBAL STATUS LIKE 'Connections';
SHOW VARIABLES LIKE 'max_connections';
SHOW FULL PROCESSLIST;Threads_connected是当前已连接线程,Threads_running更接近正在执行的线程,Max_used_connections用于观察服务启动以来的连接峰值。单看当前值可能错过短暂尖峰,因此还要结合应用日志和监控时间线。
先把连接按来源和状态分组
连接数接近上限时,一屏PROCESSLIST很难看出模式。可在权限允许时按用户、来源和命令聚合:
SELECT USER, HOST, COMMAND, COUNT(*) AS connection_count
FROM information_schema.PROCESSLIST
GROUP BY USER, HOST, COMMAND
ORDER BY connection_count DESC;关注以下差异:
- 大量Sleep:可能是连接池保留的空闲连接,也可能是连接没有正常归还;
- 大量Query且Threads_running高:数据库可能被慢查询、锁或资源瓶颈拖住;
- 单个应用主机异常突出:可能是某个实例重连风暴、任务失控或配置放大;
- 多个应用均匀增长:总连接池预算可能超过数据库承载能力;
- 来源不断创建后快速断开:应检查短连接设计、健康检查和失败重试。
Sleep并不自动等于泄漏。连接池本来就会保留一定空闲连接;关键是数量是否符合配置、负载下降后是否回落,以及连接是否能在超时与生命周期策略下被回收。
用一张预算表核对连接池
数据库最大连接数必须覆盖所有应用实例,而应用团队常只看到“每个实例最多20条”。真正的理论峰值应按实例累计:
应用A实例数 × 单实例最大池
+ 应用B实例数 × 单实例最大池
+ 定时任务与后台队列
+ 管理、迁移和监控连接
+ 必要的安全余量例如扩容应用实例后,如果数据库连接池参数没有同步调整,总池容量会按实例数放大。Kubernetes滚动发布期间,新旧实例短暂共存,也可能让连接数瞬间翻倍。这里不应套固定比例,应依据并发查询、查询耗时、数据库CPU和内存实测建立预算。
连接池泄漏常见在哪里
泄漏不是“MySQL连接没有自动消失”这么简单,通常是应用取得连接后,没有在所有代码路径归还。高风险位置包括异常分支、超时取消、流式结果未关闭、事务提前返回,以及手工管理连接但缺少可靠的清理逻辑。
排查时把这几条时间线对齐:
- 请求量是否已经下降,连接数却持续不回落;
- 连接池的已用、空闲、等待队列和获取超时是否同步异常;
- 某版本上线后,单实例连接曲线是否改变;
- 重启单个应用实例后,连接是否明显释放并再次按近似斜率增长;
- 应用日志是否出现“获取连接超时”而数据库仍有大量Sleep。
确认泄漏后应修复连接生命周期,并通过压测或故障注入验证异常路径。把数据库wait_timeout调得极短,只会让连接池拿到失效连接的概率增加,不能替代应用修复。
也可能不是连接池,而是查询退不出来
当慢查询、锁等待或磁盘延迟使每条连接占用时间变长,即使请求量没有增加,同时在库连接数也会累积。此时通常能看到Threads_running、查询时长、锁等待或响应延迟异常。
应结合慢查询日志、执行计划、锁信息和系统资源定位。不要看到一条长查询就批量KILL所有连接;终止事务可能触发回滚,进一步消耗I/O和时间。先识别业务影响、事务状态和可恢复路径。
为什么直接增大max_connections有风险
每条连接都需要线程、会话缓冲和其他资源,实际内存消耗取决于版本、查询和配置。将连接上限从一个值大幅提高,并不等于VPS立刻具备同等处理能力:
- 并发查询可能把CPU打满,整体响应更慢;
- 会话级缓冲在特定操作中增长,放大内存峰值;
- 文件描述符、线程和系统限制可能成为下一个瓶颈;
- 更多请求进入数据库后,锁竞争和磁盘队列可能恶化;
- 连接池泄漏会继续增长,只是更晚失败。
如果VPS的MemAvailable持续下降或出现Swap活动,可参考极跃圈的VPS内存缓存与泄漏判断方法,不要只根据free中的used值推算能承载多少连接。
故障中的低风险恢复顺序
- 暂停会继续放大连接的批处理、爬取、报表或非关键队列;
- 保存连接状态、应用实例数、数据库资源和错误时间点;
- 锁定连接最多或重试最激进的应用来源;
- 在具备健康检查和回滚条件时,分批处理异常应用实例;
- 若数据库仍有资源余量,可在评估内存与系统限制后做小幅临时调整;
- 恢复后修正连接池上限、获取超时、生命周期与重试退避;
- 通过相近并发验证连接峰值、查询延迟和资源曲线。
“重启全套服务”可能释放连接,却会丢失最重要的现场证据,还可能在所有实例同时启动时制造新的连接风暴。除非服务已无法管理且有明确恢复方案,否则应优先分批、可回滚地处置。
恢复后要监控的不只是当前连接数
| 指标 | 它回答的问题 |
|---|---|
| Threads_connected | 当前连接是否长期贴近上限 |
| Max_used_connections | 服务周期内峰值是否逼近上限 |
| Threads_running | 数据库是否有大量查询同时执行 |
| 连接池等待时间 | 应用是否在排队获取连接 |
| 慢查询与锁等待 | 连接为什么长时间不释放 |
| MemAvailable、CPU、I/O | 数据库是否还有真实承载余量 |
| 应用实例数与发布事件 | 连接峰值是否由扩容或滚动发布触发 |
告警阈值应留出诊断时间,并区分持续高位和瞬时尖峰。只有把数据库指标、连接池指标和发布事件放在同一时间线上,才能判断应该加连接、减池、优化查询还是扩容。
结论
MySQL报Too many connections时,不要把max_connections当成唯一旋钮。先按来源与状态拆分连接,再核对所有应用实例的连接池总预算,随后检查慢查询、锁、重试风暴和VPS资源。临时提高上限只能建立在剩余承载能力明确的前提下,长期修复仍要落到连接生命周期和数据库处理能力。






