Sleep不等于安全:挂着事务的Sleep连接会持续持有行锁、MDL读锁和undo日志空间,三者叠加可致数据库雪崩。

因为“Sleep”不等于“安全”——一个挂着事务的Sleep连接,会持续持有行锁、MDL读锁和undo日志空间,三者叠加足以让整个库雪崩。
为什么Sleep连接可能正在偷偷拖垮数据库
MySQL的Sleep状态只表示“当前没在执行SQL”,但完全无法反映事务是否已结束。应用层如果开了BEGIN或隐式事务(比如Spring的@Transactional),又因远程调用超时、逻辑卡死、异常未捕获等原因没走COMMIT/ROLLBACK,连接就会带着事务空挂在那里。
这种连接在SHOW PROCESSLIST里看着无害,实则:
- 锁住它读/写过的所有行,后续DML请求卡在
Waiting for table metadata lock或update ... waiting for lock - 阻止DDL操作(如
ALTER TABLE),因为MDL读锁不会释放 - 让
information_schema.innodb_trx里的trx_started时间越来越老,undo日志无法purge,磁盘悄悄涨满
wait_timeout不是万能解药,它只对纯空闲连接生效
wait_timeout配置项(默认8小时)只作用于“没有活跃事务”的Sleep连接。一旦事务开启,这个计时器就停摆了——哪怕连接已经Sleep了5小时,只要事务还在RUNNING,wait_timeout就完全不触发。
所以你看到大量Time值超过1000秒的Sleep连接,必须人工确认它们是否带事务:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 查
SELECT * FROM information_schema.innodb_trx WHERE TIME_TO_SEC(NOW() - trx_started) > 300 - 关联
performance_schema.threads和processlist,看对应PROCESSLIST_ID是否为Sleep - 重点盯
trx_mysql_thread_id,它对应SHOW PROCESSLIST里的Id
KILL前必须验证事务状态,否则可能丢数据
直接KILL一个ID,如果它正处在事务中间(比如刚执行完INSERT、还没COMMIT),会导致事务回滚,业务侧可能收不到失败通知,产生数据不一致。
安全操作顺序是:
- 先查
SELECT trx_state, trx_operation_state, trx_query FROM information_schema.innodb_trx WHERE trx_mysql_thread_id = ? - 若
trx_state = 'RUNNING'且trx_query IS NULL,大概率是事务挂起,可KILL - 若
trx_operation_state显示fetching rows或updating,说明SQL仍在执行,不能杀 - 生产环境建议用
KILL QUERY代替KILL,只中断当前语句,保留事务上下文供重试
真正难处理的从来不是“怎么杀”,而是“为什么会有事务挂起”。监控Sleep连接只是止血,背后暴露的是事务边界不清晰、远程调用缺乏超时兜底、连接池未配置leakDetectionThreshold等工程问题——这些不改,杀完还会再长出来。










