MySQL在并发访问较高时,经常会抛出Too many connections的错误,导致应用程序无法建立新的数据库连接,接口大面积失败。这个错误的直接含义是,当前已经存在的客户端连接数量已经达到了服务端配置的max_connections上限,MySQL拒绝再接受新的连接请求。要彻底解决这个问题,不能只靠临时调大参数,而需要从连接产生、回收和复用的全链路去分析。

一、Too many connections错误的产生原理
MySQL服务端为每个客户端连接分配一个独立的线程(或基于线程池的调度单元),并在内存中维护对应的会话状态、网络缓冲和临时对象。max_connections是一个全局只读变量,定义了允许同时存在的客户端连接最大数量。当执行SHOW PROCESSLIST看到的连接数等于该值时,下一个连接请求就会收到Too many connections报错。
很多情况下,连接数耗尽并不是因为真实业务并发真的到了极限,而是因为应用程序采用了短连接模式:每次请求都新建连接,用完不关或延迟关闭,导致大量处于Sleep状态的连接堆积。另外,如果代码异常路径没有释放连接,或者连接池最大大小设置得比数据库max_connections还大,也会迅速打满服务端连接。
二、紧急恢复手段
当线上已经出现Too many connections,首要任务是恢复服务能力。具有SUPER权限的账号不受max_connections限制,因此可以用管理员账号登录,先查看当前连接分布。
-- 查看当前连接数与最大连接数 SHOW STATUS LIKE 'Threads_connected'; SHOW VARIABLES LIKE 'max_connections'; -- 查看连接来源与状态 SELECT user, host, command, time, state FROM information_schema.processlist ORDER BY time DESC;
确认有大量闲置连接后,可以杀掉耗时过长或Sleep状态的连接。注意kill命令针对的是processlist中的Id。
-- 杀掉指定连接,Id来自processlist KILL 12345; -- 临时调大最大连接数(重启后失效) SET GLOBAL max_connections = 500;
这种调整只是缓解,若应用继续滥用连接,很快又会满。因此紧急恢复后必须马上排查代码和配置。临时调大参数也会带来内存开销,每个连接大约占用几MB到几十MB不等,需结合服务器内存评估。
三、从应用层根治连接泄漏
最常见的问题是代码中没有正确关闭连接。下面这段Java代码就存在隐患:如果发生异常,close方法不会执行,连接就泄漏了。
// 错误示例:异常时连接未关闭
public void queryWrong() throws Exception {
Connection conn = DriverManager.getConnection(url, user, pwd);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT 1");
// 如果上面抛异常,下面不会执行
rs.close();
stmt.close();
conn.close();
}
正确的做法是用try-with-resources或者finally块确保释放。这样无论是否异常,连接都会回到池中被复用或物理关闭。
// 正确示例:自动关闭
public void queryRight() throws Exception {
try (Connection conn = DriverManager.getConnection(url, user, pwd);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT 1")) {
while (rs.next()) {
// 处理结果
}
}
}
除了显式关闭,还应避免在每个方法里都新建物理连接。物理连接创建成本很高,包括TCP握手、认证和线程分配。使用连接池可以把创建好的连接缓存起来重复使用。
四、合理使用连接池
连接池是控制连接数的核心组件。以HikariCP为例,需要设置合理的最大池大小,且这个值必须小于MySQL的max_connections减去系统预留给其他服务的连接数。
HikariConfig config = new HikariConfig(); config.setJdbcUrl(url); config.setUsername(user); config.setPassword(pwd); // 最大连接数,需小于数据库max_connections config.setMaximumPoolSize(20); // 连接空闲超时,回收闲置连接 config.setIdleTimeout(600000); // 连接最大存活时间 config.setMaxLifetime(1800000); HikariDataSource ds = new HikariDataSource(config);
如果连接池最大大小设置过大,比如设成200,而数据库max_connections只有150,那么多个应用节点同时打满池子就会直接压垮数据库。建议按照公式:单节点池大小乘以节点数,再预留20%给运维和监控账号。
另外,连接池还要配置连接健康检查,避免拿到已经被MySQL服务端因wait_timeout超时关闭的死连接。大多数现代连接池都支持配置连接测试查询或启用TCP保活。
五、数据库侧参数优化
MySQL自身有两个参数和连接堆积密切相关。wait_timeout控制非交互连接空闲多久后被服务端主动关闭,interactive_timeout对应交互连接。默认常常是8小时,在Web场景里太长,可改为300秒到600秒。
SET GLOBAL wait_timeout = 600; SET GLOBAL interactive_timeout = 600;
还可以适当增大max_connections并配合thread_cache_size,让断开的连接线程被缓存复用,减少频繁创建线程的开销。但要注意,max_connections不是越大越好,它需要对应足够的内存和CPU调度能力。一般中小型业务设置为300到800之间较为稳妥。
| 参数 | 作用 | 建议值 |
|---|---|---|
| max_connections | 最大并发连接数 | 300-800 |
| wait_timeout | 空闲连接回收时间(秒) | 300-600 |
| thread_cache_size | 线程缓存数量 | 50-100 |
六、监控与长期预防
根治Too many connections不能靠出问题再处理,而要建立监控。可以通过定时采集Threads_connected和Threads_running,当连接数超过阈值的80%就报警。也可以在应用侧暴露连接池活跃连接数指标。
架构上,如果业务确实并发极高,可以考虑引入读写分离、增加从库分担读连接,或者使用ProxySQL这类中间件做连接复用和路由。这样前端应用可以保持较多逻辑连接,而后端物理连接被中间件收敛,显著降低MySQL直接面对的连接压力。
总之,Too many connections是一个系统工程问题,涉及代码规范、连接池配置和数据库参数三方协同。只有把连接当成有限且昂贵的资源去管理,才能保障服务长期稳定。
MySQLtoo_many_connections连接池修改时间:2026-08-12 00:45:34