最直接方法是执行select * from information_schema.processlist where state = 'waiting for table level lock';定位等待线程,再用show open tables where in_use > 0;找出持有锁的myisam表,最后kill connection持有锁的线程释放锁。

查 Waiting for table level lock 的线程最直接
MyISAM 表锁等待在 PROCESSLIST 中只有一种典型状态:Waiting for table level lock。这不是模糊提示,而是明确标识该线程正卡在表锁排队队列里。
执行 SELECT * FROM information_schema.PROCESSLIST WHERE STATE = 'Waiting for table level lock'; 即可捞出全部等待者。重点关注:ID(用于后续 KILL)、TIME(持续秒数,>60 秒基本可判定为异常阻塞)、INFO(为空说明没在执行 SQL,纯等锁;非空则需结合 SHOW FULL PROCESSLIST 看完整语句)。
别只盯着等待线程——它只是“受害者”,真正要找的是那个没释放锁的“加害者”。
用 SHOW OPEN TABLES 找真正持有锁的表
SHOW OPEN TABLES WHERE In_use > 0; 是定位 MyISAM 锁源头的唯一有效命令。MyISAM 没有事务视图、没有锁队列记录,In_use 值大于 0 就代表这张表当前被某个写操作(INSERT、UPDATE、DELETE、REPLACE 或显式 LOCK TABLES ... WRITE)独占。
配合以下语句确认目标表确实是 MyISAM 引擎:SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE FROM information_schema.TABLES WHERE ENGINE = 'MyISAM' AND TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema');
-
SHOW ENGINE INNODB STATUS对 MyISAM 完全无效,查了也白查 -
INNODB_TRX、INNODB_LOCKS等视图不包含 MyISAM 信息,不要浪费时间 -
SELECT本身不加锁,但只要前面有个没结束的写操作,所有后续读都会卡住——所以必须顺藤摸瓜,从等待线程的DB和INFO字段反推它想访问哪张表,再查那张表是否出现在SHOW OPEN TABLES结果中
KILL CONNECTION 而不是 KILL QUERY
MyISAM 没有死锁检测,锁释放完全依赖原操作自然结束或连接断开。KILL 操作的目标必须是持有锁的线程(State 为 Updating、Writing to net 或 Locked),不是等待者。
务必使用 KILL [ID](即 KILL CONNECTION),而不是 KILL QUERY [ID]:
-
KILL QUERY只中断当前语句,若线程刚执行完LOCK TABLES t1 WRITE还没做任何 DML,锁依然挂着 -
KILL会强制关闭连接,释放所有隐式持有的表锁 - 如果应用用了连接池,KILL 后该物理连接可能被复用,新请求仍会卡住——需同步检查应用层是否漏了
UNLOCK TABLES或连接未正常退出
避免问题比排查更重要:MyISAM 锁瓶颈本质无解
所有“优化”都是权宜之计:拆批量 INSERT、加 LOW_PRIORITY、避开高峰期 ALTER TABLE……这些只能缓解,不能根除。因为 MyISAM 表级锁机制决定了它无法支持高并发写场景。
真正关键的判断点在于:你是否还在依赖 MyISAM 特有功能?
- 全文索引:MySQL 5.6+ 的 InnoDB 已原生支持,无需妥协
- 压缩表:极少业务真需要,且现代 SSD 成本已大幅降低存储压力
- 崩溃恢复慢、无事务、无行锁——这些不是缺陷,是设计取舍。当你的业务出现锁等待,它其实是在提醒你:该迁移了
排查过程本身不难,难的是承认——有些瓶颈,换引擎才是唯一答案。











