🚨 问题现象
-
执行
UPDATE/DELETE/ALTER TABLE等操作时 长时间无响应 或报错:Lock wait timeout exceeded; try restarting transaction或
Table 'xxx' is locked -
应用写入/更新功能卡住,但读操作可能正常。
-
怀疑有数据库连接未正确关闭,导致事务未提交或表级锁未释放。
🔍 问题原因分析
- 某个应用程序(或调试脚本)开启了数据库事务但未提交或回滚;
- 连接未被显式关闭,连接池未回收 ,导致:
- 行锁 / 表锁持续持有;
- 后续操作因等待锁而阻塞;
- 常见于:
- 开发环境手动执行
BEGIN;后忘记COMMIT/ROLLBACK; - 应用代码中异常路径未关闭连接(连接泄漏);
- 长时间运行的查询未结束。
- 开发环境手动执行
💡 关键点:在 InnoDB 中,未提交的事务会持有行锁;在 MyISAM 中,写操作会直接加表锁。
🛠️ 排查步骤(以 MySQL 为例)
1. 登录数据库(使用高权限账号)
mysql -u root -p
即使连接池满,MySQL 通常仍允许一个 SUPER 用户登录。
2. 查看当前活跃连接和锁等待情况
-- 查看所有连接
SHOW PROCESSLIST;
-- 更详细信息(推荐)
SELECT
id, user, host, db, command, time, state, info
FROM information_schema.processlist
ORDER BY time DESC;
重点关注:
Time:执行/空闲时长(秒)State:如Locked,Sending data,SleepInfo:正在执行的 SQL(若为NULL且Command=Sleep,说明是空闲连接)
3. 检查是否有未提交的事务(InnoDB)
-- 查看 InnoDB 引擎状态(含锁信息)
SHOW ENGINE INNODB STATUS\G
在输出中查找 TRANSACTIONS 和 LOCKS WAITING 部分,可看到:
- 哪些事务在等待锁;
- 哪些事务持有锁;
- 对应的连接 ID(thread ID)。
4. 定位"可疑"连接
例如,发现一条连接:
User: dev_userHost: 192.168.1.100:54321Time: 3600(已空闲1小时)Info: NULLCommand: Sleep
→ 极可能是未关闭的调试连接,且可能持有未提交事务。
✅ 解决方法:终止无用连接
终止指定连接(释放锁)
KILL <connection_id>;
-- 示例
KILL 12345;
⚠️ 使用
KILL(不是KILL QUERY),确保整个连接断开,事务自动回滚,锁被释放。
批量清理(谨慎使用)
-- 生成 kill 语句(先预览)
SELECT CONCAT('KILL ', id, ';')
FROM information_schema.processlist
WHERE user = 'dev_user'
AND command = 'Sleep'
AND time > 600;
复制结果,在新窗口执行。
✅ 验证是否恢复
- 再次执行之前卡住的 SQL,确认能正常完成;
- 检查
SHOW PROCESSLIST;中不再有长时间空闲或锁定状态的连接。
🛡️ 预防措施
| 层面 | 措施 |
|---|---|
| 开发习惯 | 手动测试 SQL 时,避免只写 BEGIN; 而不提交;用完及时关闭客户端连接 |
| 应用代码 | 确保使用 try-finally 或连接池自动管理(如 HikariCP 的 close()) |
| 数据库配置 | 设置合理的超时: SET GLOBAL wait_timeout = 600; SET GLOBAL interactive_timeout = 600; |
| 监控告警 | 监控 Threads_connected 和 Innodb_row_lock_waits 指标 |
💡 经验总结
- 表锁/行锁问题往往源于"活着但无用"的连接,而非 SQL 本身性能问题;
SHOW PROCESSLIST+SHOW ENGINE INNODB STATUS是排查锁问题的黄金组合;- KILL 连接是快速恢复业务的有效手段,但需定位准确,避免误杀;
- 根本解决在于规范开发流程 + 健壮的连接管理。
📌 附:常用命令速查
-- 查看连接
SHOW PROCESSLIST;
-- 查看 InnoDB 锁状态
SHOW ENGINE INNODB STATUS\G
-- 终止连接
KILL 12345;
-- 查看超时设置
SHOW VARIABLES LIKE '%timeout%';