MySQL出现Too many connections怎么办?连接数排查、释放与参数优化教程

MySQL出现Too many connections怎么办?连接数排查、释放与参数优化教程

应用连接MySQL时出现:

ERROR 1040 (HY000): Too many connections

表示MySQL当前可用的普通客户端连接已经耗尽。网站可能因此出现数据库连接失败、500或503错误,定时任务、监控和管理工具也可能无法登录数据库。

直接提高max_connections有时能暂时恢复服务,但不一定能解决根因。连接泄漏、连接池配置过大、慢查询堆积、事务长期不提交、空闲连接过多和应用重试风暴,都可能让新增连接很快再次耗尽。更高的连接上限还会增加线程、缓冲区和文件描述符的资源压力。

本文介绍如何使用SHOW STATUSSHOW FULL PROCESSLIST和Performance Schema确认连接使用情况,安全释放异常会话,并合理调整连接池、超时参数、账号限制和max_connections

一、Too many connections表示什么

MySQL通过max_connections限制同时存在的客户端连接数量。

查看当前配置:

SHOW VARIABLES LIKE 'max_connections';

查看当前连接数:

SHOW GLOBAL STATUS LIKE 'Threads_connected';

查看正在执行任务的线程数:

SHOW GLOBAL STATUS LIKE 'Threads_running';

这两个指标含义不同:

  • Threads_connected:当前已建立的客户端连接,包括空闲连接。
  • Threads_running:当前没有休眠、正在执行工作的线程。

如果Threads_connected接近上限,但Threads_running很低,通常存在大量空闲连接或连接池规模过大。

如果两者都很高,可能存在并发高峰、慢查询、锁等待、数据库性能不足或应用重试风暴。

二、为什么管理员有时仍然可以登录

MySQL允许比max_connections多一个连接,该额外连接保留给拥有CONNECTION_ADMIN权限的账号;旧版本中可能使用SUPER权限。

救援管理员账号可以在普通连接耗尽时登录并执行:

SHOW FULL PROCESSLIST;

不要把CONNECTION_ADMIN权限授予所有应用账号。否则预留连接同样可能被普通业务占用,故障时管理员将失去排查入口。

MySQL还支持专用管理连接接口,但需要提前规划和配置,不能等连接已经耗尽后才临时依赖。

三、先保存故障现场

连接耗尽时不要立即重启MySQL。重启会中断所有业务连接、回滚未提交事务,并清除关键现场。

成功进入数据库后先执行:

SHOW GLOBAL STATUS
WHERE Variable_name IN (
  'Threads_connected',
  'Threads_running',
  'Max_used_connections',
  'Connections',
  'Aborted_connects',
  'Connection_errors_max_connections'
);

查看配置:

SHOW GLOBAL VARIABLES
WHERE Variable_name IN (
  'max_connections',
  'max_user_connections',
  'wait_timeout',
  'interactive_timeout',
  'thread_cache_size'
);

其中:

  • Max_used_connections:本次MySQL启动以来达到过的最大同时连接数。
  • Connections:MySQL启动以来的连接尝试总数。
  • Aborted_connects:连接建立失败次数。
  • Connection_errors_max_connections:因达到连接上限而失败的连接数量。

同时记录系统状态:

date
uptime
free -h
vmstat 1 5

查看MySQL服务和日志:

sudo systemctl status mysql --no-pager
sudo journalctl -u mysql --since '30 minutes ago' --no-pager

部分发行版服务名为mysqld

sudo systemctl status mysqld --no-pager
sudo journalctl -u mysqld --since '30 minutes ago' --no-pager

四、查看当前连接在做什么

执行:

SHOW FULL PROCESSLIST;

没有FULL时,SQL文本可能只显示前100个字符。

重点关注:

字段含义排查重点
Id连接标识可用于终止会话
UserMySQL账号哪个业务账号占用连接
Host客户端地址和端口哪台应用服务器连接最多
db当前数据库连接属于哪个业务库
Command当前命令类型SleepQuery
Time保持当前状态的秒数是否长期停留
State线程状态是否锁等待或执行中
Info当前SQL是否存在慢查询

MySQL 8.4可查询Performance Schema:

SELECT
    ID,
    USER,
    HOST,
    DB,
    COMMAND,
    TIME,
    STATE,
    INFO
FROM performance_schema.processlist
ORDER BY TIME DESC;

Performance Schema实现不需要传统进程列表使用的全局互斥锁,更适合繁忙实例。

五、按用户、主机和状态统计连接

按账号统计:

SELECT
    USER,
    COUNT(*) AS connections
FROM performance_schema.processlist
GROUP BY USER
ORDER BY connections DESC;

按客户端主机统计:

SELECT
    SUBSTRING_INDEX(HOST, ':', 1) AS client_host,
    COUNT(*) AS connections
FROM performance_schema.processlist
WHERE HOST IS NOT NULL
GROUP BY client_host
ORDER BY connections DESC;

按命令状态统计:

SELECT
    COMMAND,
    COUNT(*) AS connections
FROM performance_schema.processlist
GROUP BY COMMAND
ORDER BY connections DESC;

统计长时间休眠连接:

SELECT
    USER,
    SUBSTRING_INDEX(HOST, ':', 1) AS client_host,
    COUNT(*) AS sleeping_connections,
    MAX(TIME) AS longest_sleep_seconds
FROM performance_schema.processlist
WHERE COMMAND = 'Sleep'
GROUP BY USER, client_host
ORDER BY sleeping_connections DESC;

统计长时间运行的非休眠会话:

SELECT
    ID,
    USER,
    HOST,
    DB,
    COMMAND,
    TIME,
    STATE,
    INFO
FROM performance_schema.processlist
WHERE COMMAND <> 'Sleep'
ORDER BY TIME DESC;

这些统计可以快速判断故障来自某个应用节点、某个数据库账号,还是全局并发高峰。

六、大量Sleep连接是否需要删除

Sleep表示连接当前没有执行SQL,但连接仍然存在。连接池为了复用连接,保留一定数量的休眠连接是正常现象。

真正需要关注的是:

  • 休眠连接数量长期接近max_connections
  • 某个应用实例保留的连接远超业务需要。
  • 应用发布后旧连接没有释放。
  • 连接池最小空闲连接设置过大。
  • 连接使用后没有正确归还。
  • 多个应用实例各自配置了过大的连接池。

例如有20个应用实例,每个实例最大连接池为100,理论连接需求可达到2000。即使单个实例配置看起来不高,汇总后也可能超过数据库上限。

不要把所有Sleep连接都视为无用。频繁建立和销毁连接也会产生认证、线程创建和TLS握手成本。

七、如何安全释放异常连接

终止指定连接:

KILL CONNECTION 12345;

只终止当前语句、保留连接:

KILL QUERY 12345;

使用KILL前应确认:

  • 连接是否正在执行写事务。
  • 终止后是否触发长时间回滚。
  • 应用是否会立即高频重连。
  • 是否属于复制、备份、监控或系统线程。
  • 是否有明确的业务负责人。

不要直接批量终止所有连接。大事务被终止后,InnoDB回滚可能持续较长时间,并继续消耗I/O和CPU。

生成长时间休眠连接的终止语句:

SELECT CONCAT('KILL CONNECTION ', ID, ';')
FROM performance_schema.processlist
WHERE COMMAND = 'Sleep'
  AND TIME > 600
  AND USER = 'app_user';

应先检查生成结果,再选择性执行。不要把该SQL改造成无人审核的定时批量清理任务。

八、临时提高max_connections

查看当前值:

SHOW VARIABLES LIKE 'max_connections';

临时调整:

SET GLOBAL max_connections = 300;

新值只影响后续连接,并且实例重启后可能恢复配置文件中的值。

持久化方式取决于MySQL版本和部署方式。配置文件示例:

[mysqld]
max_connections = 300

修改后需要按部署环境重新加载或重启MySQL。托管数据库应通过云控制台参数组修改。

提高前必须检查:

  • 服务器物理内存和可用内存。
  • MySQL全局缓冲区大小。
  • 每连接可能分配的会话缓冲区。
  • 文件描述符和线程限制。
  • 应用真实并发需求。
  • 压力测试下的CPU和磁盘能力。

max_connections的有效上限还可能受到open_files_limit等资源约束。配置值成功写入,并不代表服务器可以稳定承载相同数量的活跃查询。

九、为什么不能根据一个固定公式设置连接数

连接内存并不是简单的“每个连接固定占用多少”。一个会话可能根据执行操作使用:

  • 线程栈。
  • 排序缓冲区。
  • Join缓冲区。
  • 读缓冲区。
  • 临时表。
  • 网络缓冲区。
  • Performance Schema相关结构。
  • 事务和存储引擎内部结构。

很多缓冲区按需分配,并非所有连接同时达到最大值。直接把所有会话变量上限相加,会严重高估;完全忽略会话内存,又可能低估并发风险。

建议根据真实工作负载压测:

  1. 记录低峰和高峰的连接数。
  2. 记录Threads_running和查询延迟。
  3. 观察MySQL进程RSS及系统可用内存。
  4. 模拟接近峰值的并发。
  5. 保留操作系统和故障恢复余量。
  6. 再决定连接上限和连接池规模。

十、调整wait_timeout要谨慎

查看:

SHOW VARIABLES LIKE 'wait_timeout';
SHOW VARIABLES LIKE 'interactive_timeout';

wait_timeout表示服务器关闭非交互式空闲连接前等待的秒数;interactive_timeout用于以交互模式建立的连接。

临时调整全局值:

SET GLOBAL wait_timeout = 600;
SET GLOBAL interactive_timeout = 600;

需要注意:

  • 全局值主要影响之后创建的新连接。
  • 已建立会话保留自己的会话值。
  • 设置过大可能让无效连接长期占位。
  • 设置过小会导致连接池中的空闲连接被服务器关闭。
  • 应用若不验证连接有效性,可能出现MySQL server has gone away
  • 连接池生命周期应与服务器超时协调。

合理方案不是简单把超时调到很小,而是修复连接归还逻辑,并让连接池具备验证、回收和重建能力。

十一、正确配置应用连接池

常见连接池参数包括:

  • 最大连接数。
  • 最小空闲连接数。
  • 获取连接超时。
  • 空闲连接回收时间。
  • 最大连接生命周期。
  • 连接有效性检查。
  • 连接泄漏检测。

数据库侧容量规划应满足:

所有应用实例最大连接池之和
+ 管理连接
+ 定时任务
+ 监控和备份
+ 数据同步与报表
+ 故障切换余量

但应用连接池总和也不应机械地等于数据库上限。需要留出管理和故障恢复空间。

推荐做法:

  • 按应用实例数量计算总连接预算。
  • 让单个实例连接池与实际线程并发匹配。
  • 限制启动时同时创建连接的速度。
  • 配置获取连接超时,避免请求无限等待。
  • 对连接泄漏开启监控。
  • 发布和扩容时防止大量实例同时建连。
  • 应用重试使用退避和随机抖动。

十二、慢查询和锁等待会耗尽连接

当查询执行时间增长时,每个请求占用连接的时间也会增长。即使访问量不变,连接数也可能快速堆积。

查看当前长查询:

SELECT
    ID,
    USER,
    HOST,
    DB,
    TIME,
    STATE,
    INFO
FROM performance_schema.processlist
WHERE COMMAND = 'Query'
  AND TIME >= 5
ORDER BY TIME DESC;

查看InnoDB事务:

SELECT
    trx_id,
    trx_mysql_thread_id,
    trx_started,
    trx_state,
    trx_query
FROM information_schema.innodb_trx
ORDER BY trx_started;

检查锁等待时,应结合:

  • Performance Schema数据锁表。
  • InnoDB事务状态。
  • 慢查询日志。
  • 应用请求链路。
  • 数据库CPU和I/O。

只提高连接数会让更多查询同时进入数据库,可能进一步放大锁竞争和磁盘压力。

十三、检查是否发生连接重试风暴

数据库短暂变慢时,应用可能不断创建新连接并立即重试,形成重试风暴。

查看连接速率:

SHOW GLOBAL STATUS LIKE 'Connections';

间隔一分钟再次查看,计算增长量。

同时查看:

SHOW GLOBAL STATUS
WHERE Variable_name IN (
  'Threads_connected',
  'Threads_running',
  'Aborted_connects'
);

典型现象:

  • Connections增长速度突然升高。
  • Threads_connected快速触顶。
  • 应用日志大量出现连接超时。
  • 多个实例在相同时间持续重试。
  • 数据库恢复后仍被重试流量压垮。

应用应使用指数退避、随机抖动、熔断和连接池获取超时,避免毫秒级无限重试。

十四、使用账号级连接限制

MySQL可以限制单个账号的同时连接数,避免一个业务耗尽全局连接。

查看全局参数:

SHOW VARIABLES LIKE 'max_user_connections';

为指定账号设置:

ALTER USER 'app_user'@'10.%'
WITH MAX_USER_CONNECTIONS 80;

查看账号定义:

SHOW CREATE USER 'app_user'@'10.%';

设置账号限制前应确认:

  • 应用实例总数。
  • 每个实例连接池大小。
  • 发布和扩容期间的峰值。
  • 后台任务是否复用同一账号。
  • 账号被限流后应用如何降级。

不同业务最好使用独立数据库账号。这样既方便权限隔离,也能按用户统计和限制连接。

十五、thread_cache_size不是连接数上限

thread_cache_size控制已结束连接对应线程的缓存数量,用于减少新连接时创建线程的成本。

查看:

SHOW VARIABLES LIKE 'thread_cache_size';

查看状态:

SHOW GLOBAL STATUS
WHERE Variable_name IN (
  'Threads_cached',
  'Threads_created',
  'Connections'
);

如果Threads_created相对于Connections增长很快,线程缓存可能偏小,或者应用频繁创建短连接。

但提高thread_cache_size不会增加可用客户端连接,也不能修复连接泄漏。它解决的是连接线程复用成本,不是Too many connections本身。

十六、检查操作系统资源限制

提高连接上限后,还要检查MySQL进程限制:

pid=$(pidof mysqld)
cat /proc/$pid/limits

查看文件描述符:

sudo ls /proc/$(pidof mysqld)/fd | wc -l

查看系统文件句柄:

cat /proc/sys/fs/file-nr

查看systemd配置:

systemctl show mysql |
  grep -E 'LimitNOFILE|TasksMax'

服务名也可能是mysqld

需要关注:

  • open files限制。
  • systemd的LimitNOFILE
  • 可用内存。
  • 线程和任务数量限制。
  • TCP连接队列。
  • 端口和网络资源。

操作系统限制不足时,单独调整MySQL参数可能无法达到预期值。

十七、应急处理顺序

当业务已经大面积连接失败,可以按下面顺序处理:

  1. 使用具有CONNECTION_ADMIN权限的救援账号登录。
  2. 保存连接数、进程列表、资源和日志现场。
  3. 确认是空闲连接堆积、慢查询还是重试风暴。
  4. 限制异常应用实例或入口流量。
  5. 选择性终止明确异常的连接或查询。
  6. 必要时小幅临时提高max_connections争取处理时间。
  7. 检查内存和文件描述符余量。
  8. 修复连接池、SQL、锁等待或应用重试。
  9. 将合理参数写入持久配置。
  10. 持续观察连接数、活跃线程和错误次数。

如果已经无法获得管理员连接,可通过本机Socket、专用管理接口或托管数据库控制台处理。重启MySQL应作为最后手段,并评估事务回滚、复制和业务中断影响。

十八、建议建立哪些监控

建议持续监控:

  • Threads_connected
  • Threads_running
  • Max_used_connections
  • Connection_errors_max_connections
  • Connections
  • Aborted_connects
  • 各账号和各应用主机连接数
  • 长时间Sleep连接
  • 长查询和长事务
  • 锁等待
  • 连接获取耗时
  • 连接池使用率和等待队列
  • MySQL进程内存、CPU和文件描述符
  • 数据库重启和连接失败率

告警不应等到连接达到100%才触发。可以根据业务峰值,在达到连接预算的一定比例、连接增长速度异常或连接池等待时间上升时提前告警。

十九、推荐的完整排查流程

  1. 确认错误确实是ERROR 1040: Too many connections
  2. 使用救援管理员账号进入数据库。
  3. 查看max_connectionsThreads_connectedThreads_running
  4. 保存Max_used_connections和连接错误指标。
  5. 使用Performance Schema按用户、主机和状态统计连接。
  6. 检查大量Sleep连接及连接池配置。
  7. 检查慢查询、长事务和锁等待。
  8. 检查应用是否发生高频连接重试。
  9. 限制异常流量并选择性释放会话。
  10. 评估内存和文件描述符后再调整max_connections
  11. 协调wait_timeout与连接池生命周期。
  12. 使用账号级连接限制隔离业务。
  13. 修复应用连接泄漏和重试策略。
  14. 建立连接数、活跃线程和错误率监控。

总结

MySQL出现Too many connections,说明普通客户端连接已经达到可用上限。排查时应先区分连接总数高和活跃查询高:大量休眠连接通常指向连接池或连接泄漏,活跃线程同时升高则需要进一步检查慢查询、锁等待、资源瓶颈和流量高峰。

max_connections可以临时调整,但不能脱离内存、文件描述符和数据库处理能力无限提高。更可靠的解决方案是根据所有应用实例计算连接预算,合理配置连接池和超时,为不同业务设置独立账号及连接限制,并保留管理员救援入口。

最终目标不是让MySQL接受尽可能多的连接,而是用可控数量的连接稳定完成更多请求。

知识库

Linux服务器时间不准怎么办?Chrony时间同步、时区配置与故障排查教程

2026-8-13 16:57:37

知识库

域名解析不生效怎么办?DNS记录、缓存、TTL与解析故障排查教程

2026-8-14 18:20:15