本质是应用层未正确关闭连接,非数据库配置问题;需查information_schema.processlist中time>60且command='sleep'的连接,结合host定位应用服务器,并检查代码是否在异常路径遗漏close()、连接池参数是否与wait_timeout错配。

MySQL中Sleep连接过多,本质是应用层没有正确关闭连接,不是数据库配置问题;调大wait_timeout只会掩盖问题,不能解决连接池耗尽。
怎么看哪些Sleep连接在拖后腿
直接查information_schema.PROCESSLIST,重点关注Time值大、Command为Sleep、且State为空的连接:
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO FROM information_schema.PROCESSLIST WHERE COMMAND = 'Sleep' AND TIME > 60 ORDER BY TIME DESC LIMIT 20;
注意:TIME单位是秒,超过wait_timeout(默认28800)会被自动KILL,但若设得过大(比如设置成数小时),这些连接就会长期滞留;HOST列能快速定位是哪个应用服务器发起的,配合日志可反查对应服务。
为什么连接没释放?常见代码陷阱
绝大多数Sleep连接堆积源于应用未显式关闭连接,尤其在异常路径或连接复用逻辑里被忽略:
- Java中
Connection未在finally块或try-with-resources中close(),特别是DAO方法抛出异常后跳过关闭逻辑 - Python使用
pymysql或mysql-connector-python时,忘记调用connection.close(),或在except后直接return而跳过清理 - PHP的
mysqli或PDO在函数中途return前未$conn->close(),或误以为脚本结束会自动释放(CLI模式下可能延迟释放) - 连接池配置不当:如HikariCP的
max-lifetime远大于wait_timeout,导致连接在池中“活”过期,归还时仍是Sleep状态
怎么快速止血并验证根因
先临时清理积压连接,再通过监控确认是否复发:
- 批量KILL Sleep连接(慎用,避免误杀业务活跃连接):
KILL <id>; -- 或用脚本生成 KILL 语句</id>
- 检查当前连接总数是否回落:
SHOW STATUS LIKE 'Threads_connected'; - 开启慢查询日志 + general_log(临时)观察新连接的
Connect/Quit行为,确认是否有连接建立后从不Quit - 在应用侧加日志:在获取连接和归还连接处打点,统计连接生命周期,看是否存在“获取后无归还”情况
wait_timeout 和 interactive_timeout 到底怎么设
这两个参数不是调优项,而是兜底安全阀;设得太大会让问题更难暴露,设得太小又可能误杀合法长事务:
-
wait_timeout作用于非交互式连接(如JDBC、PHP mysqli),建议设为300(5分钟)——足够覆盖多数业务SQL执行+网络往返,又不至于让泄漏连接滞留太久 -
interactive_timeout作用于交互式连接(如mysql client),可设为28800(8小时),影响小 - 务必确保应用连接池的
idleTimeout(如HikariCP)小于wait_timeout,否则连接池会把已超时的连接继续复用,触发MySQL server has gone away - 修改后需重启应用(非MySQL服务),因为已有连接沿用旧timeout值
真正棘手的不是Sleep连接本身,而是它背后那个“以为连接用完就自动关”的假设——数据库不会替你管理应用层资源,只要有一处close()被跳过,它就会在PROCESSLIST里安静地躺到超时。











