僵尸连接是客户端未正常断开、服务端仍保留的空闲连接,表现为State为Sleep且Time远超业务合理时长(如>300秒),占用max_connections配额;可通过SELECT查询information_schema.processlist识别,结合time与业务场景判断,并通过调低wait_timeout和interactive_timeout至300~600秒根治。

MySQL连接没断但卡住,怎么识别是僵尸连接
僵尸连接本质是客户端没正常断开、服务端还留着的空闲连接,表现为 State 为 Sleep 且 Time 值远超业务合理等待时长(比如 > 300 秒)。它不报错,但会占 max_connections 配额,拖慢新连接建立。
查法很简单:
SELECT id, user, host, db, command, time, state, info FROM information_schema.processlist WHERE command = 'Sleep' AND time > 300;
-
time是秒数,单位固定,不是毫秒 - 别只看
State = 'Sleep'—— 很多健康长连接(如连接池保活)也是 Sleep,得结合time和业务场景判断 - 如果
info为NULL,基本可确认没在执行语句,更可能是僵尸
用 wait_timeout 和 interactive_timeout 控制自动断连
这两个参数才是根治源头的配置项,不是“定时 kill”的替代方案。它们决定空闲连接存活多久后被服务端主动断开。
关键差异:
-
wait_timeout:影响非交互式连接(比如应用服务器用 JDBC 连接池建的连接) -
interactive_timeout:影响交互式连接(比如你用 mysql 客户端连上去敲命令) - 两者默认都是 28800 秒(8 小时),远高于大多数 Web 应用的合理空闲阈值
- 修改后只对新连接生效,已有僵尸连接不受影响
建议设为 300~600 秒(5~10 分钟),写进 my.cnf:
[mysqld] wait_timeout = 300 interactive_timeout = 300
手动 kill 僵尸连接要避开这些坑
直接 KILL [id] 最快,但容易误杀或失败:
- 不能
KILL自己的连接(KILL CONNECTION [id]也不行),否则会断开当前会话 -
KILL QUERY [id]只中断当前语句,对 Sleep 状态无效 - 如果连接正在锁表或事务中,
KILL后可能触发回滚,延迟释放资源 - 高并发下批量
KILL容易引发短暂性能抖动,别一口气干掉上百个
安全做法是加条件过滤再批量生成 kill 语句:
SELECT CONCAT('KILL ', id, ';')
FROM information_schema.processlist
WHERE command = 'Sleep' AND time > 600 AND user != 'system user';
复制结果执行即可,排除了系统内部线程和活跃用户。
为什么不要依赖定时任务定期 kill
写个脚本每分钟扫一遍 processlist 并 kill,看似省事,实则掩盖问题且有副作用:
- 无法区分“真僵尸”和“合理长连接”(比如某些报表导出需保持连接 15 分钟)
- 频繁查询
information_schema.processlist在连接数大时本身就有性能开销 - kill 行为不可逆,一旦误判就导致业务请求失败,比连接耗尽更难排查
- 根本没解决客户端不 close、连接池配置不当等上游问题
真正该做的,是调低 wait_timeout,同时检查应用层是否用了连接池、池的 maxIdle 和 minIdle 是否合理,以及有没有漏掉 connection.close()。
配置生效后,僵尸连接会在空闲超时后自然消失,不需要你动手 —— 手动 kill 只该是临时救火,不是常态策略。










