不能仅凭慢查询日志确认myisam表锁等待,因其query_time记录执行耗时而非锁等待时间;需结合rows_examined异常偏高、多条查询密集堆积于同一表、show open tables中in_use>0及show processlist中state='waiting for table level lock'综合判断。

如何确认慢查询日志里存在 MyISAM 表锁等待?
MySQL 慢查询日志本身不直接记录“锁等待类型”,但 MyISAM 的表级锁阻塞会在日志中留下关键线索:执行时间远超语句本身实际耗时,且 Query_time 显著大于 Lock_time(注意:此处的 Lock_time 是 MySQL 5.6+ 中记录的“获取锁所花时间”,对 MyISAM 来说,它往往极小甚至为 0,而真正卡住的是“等锁释放”的过程——这部分不计入 Lock_time,却全算进 Query_time)。
更可靠的判断方式是结合日志中的 Rows_examined 和执行上下文:
- 如果一条简单 SELECT * FROM t WHERE id = ? 查了上百万行、Query_time 达数秒,但 Rows_sent 只有 1 行 → 很可能被写操作(如 INSERT/UPDATE)长期持有表锁阻塞;
- 日志中连续出现多条来自同一 MyISAM 表的慢查询,且时间戳密集堆积 → 典型锁队列现象。
为什么不能只看 slow_query_log + long_query_time?
MyISAM 表锁导致的“慢”,本质是排队等待,不是语句执行慢。默认配置下,只有语句真正执行完才记入慢日志,而等待锁的过程不触发计时起点——也就是说,如果一个 UPDATE 被前面的长事务卡住 10 秒才开始执行,它自身执行只用 0.02 秒,就不会被记录为慢查询,但下游所有读都会因此变慢。
必须开启额外诊断能力:
- 设置 log_queries_not_using_indexes = ON(配合 long_query_time = 0 可捕获全部查询,便于分析锁竞争热点);
- 启用 performance_schema 并打开相关 instrument(如 wait/lock/metadata/sql/mdl),比慢日志更早暴露锁等待;
- 关键点:slow_query_log 单独无法定位“锁等待型慢”,它只能帮你发现“已发生的慢”,而真正的问题源头往往不在日志里那条慢 SQL 本身。
如何用 SHOW OPEN TABLES 快速验证 MyISAM 表是否被锁住?
当怀疑某张 MyISAM 表(比如 myapp_logs)正被锁住时,直接查元数据比翻日志更快:
SHOW OPEN TABLES WHERE `Table` = 'myapp_logs' AND `Database` = 'your_db';
重点关注 In_use 列:
- In_use > 0 表示该表当前被某个线程以写锁(或排他锁)打开,其他写操作必须等待;
- 若 In_use = 1 且长时间不动,大概率是某个连接崩溃未释放锁(MyISAM 不支持事务回滚,异常断连可能导致锁残留);
- 注意:SHOW PROCESSLIST 中对应线程的 State 常显示为 Locked 或空,而非具体动作,这是 MyISAM 锁的典型表现。
补充技巧:
- 对疑似阻塞的线程,用 KILL [id] 强制释放锁(MyISAM 下有效,但会中断其当前操作);
- 不要依赖 FLUSH TABLES 清锁——它会强制关闭所有表,引发更大范围阻塞。
为什么混合引擎环境下更容易踩坑?
常见误操作是把 InnoDB 和 MyISAM 表混在同一个业务逻辑里,例如:一个事务先更新 InnoDB 订单表,再往 MyISAM 日志表插入一条记录。此时,MyISAM 的写操作会立即加表锁,而该锁不会被事务管理,也不会随事务回滚释放——哪怕前面的 InnoDB 更新已回滚,MyISAM 锁仍挂着,后续所有对该表的读写都被堵住。
这种场景下,慢日志里看到的可能是正常的 SELECT,但根源是上游某个早已结束的“半截事务”遗留的 MyISAM 锁。
- 解决方向:避免在事务中操作 MyISAM 表;
- 替代方案:用 INSERT DELAYED(已废弃)不如直接迁移到 InnoDB;
- 最容易被忽略的一点:应用层重试逻辑若没设超时,会让多个请求在 MyISAM 表锁前堆成雪球,而日志只记录最后几个“超时失败”的查询,根本看不到第一个锁源。











