凌晨一点,业务报警不是慢查询,而是连接失败:ERROR 1040 (HY000): Too many connections。很多人第一反应是把 max_connections 从 800 调到 2000。我一般会先拦一下,因为这类事故最怕把保险丝换成铁丝。
MySQL 8.x 里,max_connections 决定普通客户端连接上限;到达上限后,新的普通连接会被拒绝。MySQL 8 也提供管理连接相关能力,方便在连接打满时保留排障入口。本文不讲客户端框架,只站在 MySQL 侧,把连接池雪崩怎么复现、怎么诊断、怎么上线收口讲清楚。

先复现:为什么小流量也能打满连接
假设一个订单服务有 24 个副本,每个副本连接池最大 50,光这一个服务理论上就能占 1200 条连接。如果 MySQL 实例 max_connections=1000,再加上后台任务、报表、运维连接和复制连接,事故已经写在配置里了。
SHOW VARIABLES LIKE 'max_connections'; SHOW GLOBAL STATUS LIKE 'Threads_connected'; SHOW GLOBAL STATUS LIKE 'Threads_running'; SHOW GLOBAL STATUS LIKE 'Max_used_connections'; SHOW GLOBAL STATUS LIKE 'Connection_errors_max_connections';
这里要分清两个指标:Threads_connected 高,说明连接占用多;Threads_running 高,说明正在执行的线程多。如果 connected 高但 running 不高,常见原因是连接池开太大或连接泄漏。如果 running 也高,就要继续查慢 SQL、锁等待和长事务。
事故现场先保命:别把 DBA 挡在门外
连接打满时,最尴尬的是应用连不上,DBA 也连不上。MySQL 8 可以规划管理连接入口,生产上至少要有一条排障通道和一个不被业务连接池使用的管理账号。没有这条通道,现场只能等连接自然释放,或者冒险杀进程。

定位来源:谁占住了连接
连接问题不要只看总数,要看账号、来源主机、状态和持续时间。下面这些 SQL 足够在现场先摸清方向。
SELECT user, host, db, command, time, state FROM information_schema.processlist ORDER BY time DESC LIMIT 30; SELECT user, host, current_connections, total_connections FROM sys.user_summary_by_statement_latency ORDER BY current_connections DESC LIMIT 20;
如果大量连接处于 Sleep,先查连接池最小空闲、最大连接、空闲回收和泄漏。如果大量连接卡在同一种 state,比如 waiting for lock 或 sending data,就不要急着调连接数,先回到 SQL、索引和事务。

连接池容量怎么估
我的经验是反过来算:先给 MySQL 实例留出管理、复制、定时任务和临时排障余量,再把剩余连接按服务重要性分配。不要让所有服务的连接池最大值简单相加后超过实例上限。
可分配业务连接 = max_connections - 管理余量 - 复制/任务余量 单服务池上限 = floor(服务连接预算 / 服务副本数)
例如 max_connections=1000,预留 100 给管理和后台,核心订单服务 30 个副本,如果给它 450 条预算,每个副本池上限就不应该超过 15。这个数字看着保守,但比 30 个副本各开 50 然后一起冲垮数据库可靠得多。
什么时候才调大 max_connections
只有在确认 CPU、内存、线程调度、SQL 延迟都能承受,并且连接数上限确实成为瓶颈时,我才会调大 max_connections。调之前要评估每条连接的内存开销、峰值并发 SQL、线程栈和临时内存风险。否则你只是允许更多请求进入数据库排队。
SET PERSIST max_connections = 1200; SHOW VARIABLES LIKE 'max_connections';
生产上我更推荐先调连接池和超时:降低最大池、缩短空闲回收、设置合理连接获取超时,并在熔断侧限制重试风暴。MySQL 侧调大上限通常是配套动作,不是第一动作。
上线检查清单
- 确认所有服务副本数和连接池最大值,算出理论最大连接总和。
- 为 DBA、复制、备份、迁移任务预留连接,不要让业务池吃满。
- 监控
Threads_connected、Threads_running、Max_used_connections和连接错误计数。 - 对连接获取失败设置退避,避免失败后立刻重试形成风暴。
- 压测时同时观察 SQL 延迟、锁等待和活跃线程,不只看 QPS。
- 如果调整
max_connections,同步评估内存和线程调度风险。
总结
Too many connections 不是一句“数据库连接数太小”就能解释。它经常是连接池配置、服务副本数、慢 SQL、长事务和重试策略叠在一起的结果。真正稳的处理方式是:先保住排障入口,再拆连接来源,最后让连接池预算和 MySQL 承载能力对齐。
如果你只把 max_connections 调大,短期可能不报警,长期会让更多请求挤进数据库,把慢 SQL 和锁等待放大。把连接当容量资源来治理,才是高并发 MySQL 系统该有的基本功。