MySQL 连接数过多导致服务不可用处理

当 MySQL 因连接数过多而拒绝服务时,别急着重启。本文从实际问题切入,通过 SHOW STATUS、SHOW PROCESSLIST 等命令快速定位连接来源,并介绍调整 max_connections、使用连接池、优化应用代码等处理方案,附带预防措施与常见误区。

MySQL 连接数过多导致服务不可用处理
封面图:ZuCDN · ZuCDN 原创

你的 MySQL 服务突然无法访问,应用报错“Too many connections”,这就是 MySQL 连接数过多 的典型症状。别急着重启数据库,先冷静下来,按下面的步骤排查和处理,往往能在几分钟内恢复服务,并找到根因避免再次发生。

第一步:确认当前连接数是否真的达到上限

首先,你需要确认连接数是否真的达到了上限。如果还能通过命令行连上 MySQL(比如使用 root 账号),执行以下命令查看当前连接数和最大连接数:

SHOW STATUS LIKE 'Threads_connected';
SHOW VARIABLES LIKE 'max_connections';

如果 Threads_connected 接近或等于 max_connections(默认值通常是 151),那么问题就确认了。但有时应用报错,而数据库端连接数并不高,那可能是连接被拒绝或网络问题,需要进一步排查。

第二步:找出谁占用了连接

确认连接数过多后,下一步是找出哪些连接占用了资源。执行:

SHOW FULL PROCESSLIST;

这个命令会列出所有当前连接,重点关注 Command 列(如 Query、Sleep)和 Info 列(执行的 SQL)。常见情况:

  • 大量 Sleep 连接:应用没有正确释放连接,导致连接池中的空闲连接堆积。
  • 大量 Query 连接:可能有慢查询或并发过高,需要优化 SQL 或增加索引。
  • 特定来源 IP 的连接过多:可能是某个应用服务器或恶意扫描,考虑限制来源。

如果连接数太多,SHOW PROCESSLIST 也可能执行不了,这时可以查询 information_schema 表:

SELECT * FROM information_schema.processlist ORDER BY time DESC;

第三步:紧急处理:杀掉多余连接

如果服务已经不可用,你需要快速释放连接。可以手动杀掉一些空闲连接:

KILL [CONNECTION] id;

但逐个杀太慢,可以使用如下 SQL 生成批量 kill 语句(注意替换条件):

SELECT CONCAT('KILL ', id, ';') FROM information_schema.processlist WHERE command = 'Sleep' AND time > 100;

然后把结果复制执行。注意:不要杀掉正在执行重要事务的连接,否则可能导致数据回滚。如果连接数实在太多,且无法通过 SQL 处理,可以考虑临时重启 MySQL,但这应该是最后手段,因为重启会中断所有事务,且可能掩盖真正的根因。

第四步:调整 max_connections 参数

如果确认业务确实需要更多连接,可以临时调大 max_connections

SET GLOBAL max_connections = 500;

但请注意,这个设置重启后失效,需要写入配置文件(如 my.cnf 或 my.ini)中的 [mysqld] 段:

max_connections = 500

调大连接数并非万能。每个连接都会占用内存(通常几百 KB 到几 MB),如果服务器内存不足,反而可能导致系统崩溃。因此,调大连接数前,请先评估服务器内存和负载。一般来说,max_connections 不要超过 1000,具体要根据硬件和业务情况测试。

第五步:从应用层优化连接使用

根治连接数过多,必须从应用层入手。最常见的原因是应用没有使用连接池,或者连接池配置不合理。例如,在 Python 中使用 mysql-connector-python 时,如果每次请求都新建连接,并发一高就会打满连接数。正确的做法是使用连接池,如 SQLAlchemyQueuePool,或 django-db-connection-pool 等。连接池可以复用连接,并限制最大连接数,避免应用无限创建连接。

另外,检查应用代码中是否有连接泄漏:比如查询后没有关闭连接或归还给连接池。这通常表现为数据库端大量 Sleep 连接。使用 Python 的 logging 模块记录连接创建和关闭的日志,有助于定位泄漏点。正如 Python 官方日志文档 所强调的,良好的日志记录能提供关键线索。

第六步:监控与预防

为了防止再次发生,你需要建立监控和预警。至少监控以下指标:

  • Threads_connected:当前连接数
  • Threads_running:正在执行的线程数,如果这个值过高说明有慢查询或 CPU 瓶颈
  • Aborted_connects:连接失败次数,如果持续增长说明可能有连接被拒绝

可以使用 Prometheus + Grafana 等工具采集这些状态变量,并设置告警阈值,例如当连接数达到 max_connections 的 80% 时发出警告。另外,日志记录也是监控的一部分。根据 OWASP 日志安全速查表 的建议,应用日志应记录关键事件,包括连接失败等安全事件,这有助于事后分析。

常见误区与注意事项

  • 误区一:一味调大 max_connections。这可能导致内存耗尽,系统变慢甚至 OOM。正确做法是结合连接池和限流。
  • 误区二:重启 MySQL 解决一切。重启会清掉所有连接,但根因未除,很快又会满。而且重启会导致未提交事务回滚,可能造成数据不一致。
  • 误区三:忽略慢查询。一条慢查询可能长时间占用连接,导致其他请求等待。开启慢查询日志,定期分析。
  • 注意:在紧急处理时,杀掉连接可能影响正在进行的业务,务必先通知相关团队。

总结

处理 MySQL 连接数过多,核心是“先恢复,再根治”。通过查看状态、定位来源、清理连接快速恢复服务,然后通过调整配置、优化应用、建立监控来防止复发。记住,连接数只是表象,真正的根因往往是应用层的连接管理问题。另外,可以参考 OpenTelemetry 日志规范,将连接数等指标纳入统一的观测体系,实现更高效的排查。

参考资料

延伸阅读