MySQL报Too many connections,先加max_connections还是先查连接池?

连接上限是闸门,连接池预算、慢查询和资源余量才决定怎么修
发布于
4

网站突然大量返回数据库连接失败,日志里出现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值推算能承载多少连接。

故障中的低风险恢复顺序

  1. 暂停会继续放大连接的批处理、爬取、报表或非关键队列;
  2. 保存连接状态、应用实例数、数据库资源和错误时间点;
  3. 锁定连接最多或重试最激进的应用来源;
  4. 在具备健康检查和回滚条件时,分批处理异常应用实例;
  5. 若数据库仍有资源余量,可在评估内存与系统限制后做小幅临时调整;
  6. 恢复后修正连接池上限、获取超时、生命周期与重试退避;
  7. 通过相近并发验证连接峰值、查询延迟和资源曲线。

“重启全套服务”可能释放连接,却会丢失最重要的现场证据,还可能在所有实例同时启动时制造新的连接风暴。除非服务已无法管理且有明确恢复方案,否则应优先分批、可回滚地处置。

恢复后要监控的不只是当前连接数

指标 它回答的问题
Threads_connected 当前连接是否长期贴近上限
Max_used_connections 服务周期内峰值是否逼近上限
Threads_running 数据库是否有大量查询同时执行
连接池等待时间 应用是否在排队获取连接
慢查询与锁等待 连接为什么长时间不释放
MemAvailable、CPU、I/O 数据库是否还有真实承载余量
应用实例数与发布事件 连接峰值是否由扩容或滚动发布触发

告警阈值应留出诊断时间,并区分持续高位和瞬时尖峰。只有把数据库指标、连接池指标和发布事件放在同一时间线上,才能判断应该加连接、减池、优化查询还是扩容。

结论

MySQL报Too many connections时,不要把max_connections当成唯一旋钮。先按来源与状态拆分连接,再核对所有应用实例的连接池总预算,随后检查慢查询、锁、重试风暴和VPS资源。临时提高上限只能建立在剩余承载能力明确的前提下,长期修复仍要落到连接生命周期和数据库处理能力。

常见问题(FAQ)

MySQL报Too many connections可以直接重启吗?
重启可能暂时释放连接,但会丢失现场并可能触发应用重连风暴。应先保存连接来源、状态、实例数和资源快照,具备恢复方案后再分批处置。
大量Sleep连接就是连接池泄漏吗?
不是。连接池会保留空闲连接。要看数量是否符合配置、负载下降后是否回落,以及异常路径是否没有归还连接。
max_connections调得越大越好吗?
不是。更多连接会消耗线程、会话内存和文件描述符,也可能加剧CPU、锁和磁盘竞争,必须结合VPS资源与查询并发小幅评估。
连接池最大值应该怎么计算?
应累计所有应用实例、定时任务、队列、监控和管理连接,并考虑滚动发布期间新旧实例共存,再保留必要的运维余量。

本文由作者原创/授权发布于极跃圈(jiyueip.com)未经许可,禁止转载。题图来自Unsplash,基于CC0协议。

声明:极跃圈(JIYUEIP.com)内网友所发表的所有内容及言论仅代表其本人,并不反映任何极跃圈(JIYUEIP.com)之意见及观点。

0 讨论
热门最新
总结
暂无总结
0 / 600