
1. 问题现象与初步排查那天早上刚到公司就收到监控系统发来的告警短信MySQL服务器连接数超过阈值。登录服务器一看果然连接数已经接近max_connections的设定值默认是151。应用日志里大量报错Too many connections部分业务功能已经开始出现超时。首先我执行了show processlist命令查看当前连接情况发现大量连接处于Sleep状态而且很多连接的Time值已经超过了几千秒正常情况下业务连接应该在秒级完成。更奇怪的是这些Sleep连接都来自同一个应用服务器。关键提示当连接数暴增时第一步永远是先看processlist重点关注State列和Time列Sleep状态的连接如果长时间不释放就是问题所在。2. 连接泄漏的定位过程2.1 锁定问题应用通过processlist中的host信息我很快定位到了那台异常的应用服务器。登录该服务器后先用netstat统计了到MySQL的实际连接数netstat -ant | grep 3306 | wc -l结果显示有近200个TCP连接远超该应用正常情况下的连接池配置我们用的是HikariCP配置的最大连接数是50。这说明要么连接池配置没生效要么存在连接泄漏。2.2 检查连接池配置查看应用配置确认HikariCP参数spring.datasource.hikari.maximum-pool-size50 spring.datasource.hikari.leak-detection-threshold60000配置看起来没问题但leak-detection-threshold设得偏高1分钟可能难以及时发现泄漏。我临时把它调整为10秒后重启应用果然很快在日志中看到了连接泄漏警告。2.3 分析泄漏代码通过泄漏警告中的堆栈信息很快定位到一个订单查询服务中存在未关闭的连接public ListOrder queryOrders(Long userId) { Connection conn dataSource.getConnection(); // 执行查询但未在finally中关闭连接 return orderMapper.queryByUser(conn, userId); }这是个典型的连接泄漏案例——获取连接后没有用try-with-resources或finally块确保关闭。3. 深入分析与解决方案3.1 为什么连接会堆积MySQL默认不会主动关闭Sleep连接直到达到wait_timeout默认8小时。而我们的应用每小时会创建约3000个新连接导致连接池达到上限后新请求需要等待部分请求超时后会重试加剧问题最终所有连接都被占用完全无法服务3.2 临时解决方案先调低MySQL的wait_timeout临时改为60秒SET GLOBAL wait_timeout 60;杀掉现有Sleep连接SELECT CONCAT(KILL ,id,;) FROM information_schema.processlist WHERE Command Sleep AND Time 60 INTO OUTFILE /tmp/kill.sql; SOURCE /tmp/kill.sql;3.3 长期解决方案修复代码中的连接泄漏public ListOrder queryOrders(Long userId) { try (Connection conn dataSource.getConnection()) { return orderMapper.queryByUser(conn, userId); } }优化连接池配置# 缩短泄漏检测时间 spring.datasource.hikari.leak-detection-threshold5000 # 添加连接健康检查 spring.datasource.hikari.connection-test-querySELECT 1添加监控告警监控MySQL的Threads_connected指标设置应用层连接获取超时告警4. 预防措施与最佳实践4.1 代码层面统一使用try-with-resources管理连接禁止在业务代码中直接获取连接统一使用ORM框架代码审查时重点关注资源关闭逻辑4.2 配置层面合理设置连接池参数最大连接数不超过MySQL的max_connections的80%泄漏检测阈值建议5-10秒MySQL服务器优化# 适当调低wait_timeout SET GLOBAL wait_timeout 300; # 启用连接数监控 SET GLOBAL thread_pool_size 16;4.3 监控体系关键指标监控Threads_connected / max_connections 比例连接获取等待时间连接存活时间分布建立分级告警警告级别连接数 70%严重级别连接数 90%5. 排查工具箱5.1 常用诊断命令查看连接状态SHOW STATUS LIKE Threads_%; SHOW PROCESSLIST;查看连接来源统计SELECT host, COUNT(*) FROM information_schema.processlist GROUP BY host;查看连接存活时间SELECT TIME, USER, HOST, DB, COMMAND FROM information_schema.processlist ORDER BY TIME DESC;5.2 性能日志分析开启慢查询日志SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 1;检查连接等待事件SELECT * FROM performance_schema.events_waits_current WHERE EVENT_NAME LIKE wait/io/socket%;5.3 连接池健康检查对于Java应用可以通过JMX检查连接池状态jconsole pid查看HikariPool的活跃连接、空闲连接、等待线程数等指标6. 经验总结连接泄漏往往表现为连接数缓慢增长大量Sleep状态的连接应用响应变慢但CPU/内存不高一定要在连接池配置泄漏检测阈值不要设太高生产环境建议wait_timeout设置在5-10分钟连接数监控要包含应用层和数据库层这次事故让我深刻认识到数据库连接这种稀缺资源必须像对待文件描述符一样谨慎管理。现在我们团队在代码审查时会把资源关闭作为重点检查项同时建立了完善的连接数监控体系。