lock tables 会必然阻塞所有未获锁的并发请求:write锁完全独占表(读写均阻塞),read锁允许并发读但禁止写;锁需显式unlock tables或会话断开才释放,否则长期残留。

LOCK TABLES 会直接阻塞所有未获得锁的并发请求,不是“可能导致”,而是必然发生排队 —— 这是表级锁本身的语义决定的。
为什么 LOCK TABLES 一执行,后续请求就全卡住?
表级锁(尤其是 WRITE 锁)是排他性的:只要一个会话执行了 LOCK TABLES t1 WRITE,其他任何会话对 t1 的读或写操作(包括 SELECT、UPDATE、INSERT)都会被挂起,进入 Waiting for table level lock 状态,直到锁被释放。
这不是 MySQL 的 bug,而是 MyISAM 引擎的原生行为,也是 InnoDB 在显式使用 LOCK TABLES 时的退化表现 —— 此时 InnoDB 会放弃行锁能力,退回到表级锁定逻辑。
-
READ锁允许并发读,但禁止任何写;WRITE锁则彻底独占,连读都不让 - 锁不会随事务自动释放,必须显式执行
UNLOCK TABLES或者会话断开 - 如果执行
LOCK TABLES后忘了UNLOCK,或者会话异常中断未清理,锁可能长期残留
哪些操作会意外触发表级锁?
很多人以为只有手动 LOCK TABLES 才会表锁,其实以下场景也会强制升级为表级锁:
- 在没有合适索引的条件下执行
UPDATE或DELETE,InnoDB 无法定位具体行,转而扫描全表并加意向排他锁(IX),但若查询条件无法利用索引,优化器可能放弃行锁逻辑,实际效果等同于表锁 - 使用 MyISAM 引擎的表 —— 它压根不支持行锁,所有 DML 都是表级锁
- 执行
ALTER TABLE(尤其在 5.7 中未开启ALGORITHM=INPLACE)会先获取 MDL 写锁,同时隐式触发表级结构锁,阻塞所有对该表的访问
SHOW PROCESSLIST 里看到 Waiting for table level lock 怎么快速定位?
这不是行锁等待,也不是 MDL 锁等待,而是真正的表级锁阻塞,排查路径很直接:
- 先查谁在持有锁:
SHOW OPEN TABLES WHERE In_use > 0;—— 返回结果中In_use值大于 0 的表,就是被显式锁住或因引擎/无索引被迫锁表的表 - 再查阻塞源头:
SHOW PROCESSLIST;找出State是Locked或长时间Waiting for table level lock的线程,看它们的Info字段是否含LOCK TABLES或慢UPDATE/DELETE - 注意区分:
Waiting for table metadata lock是 MDL 锁(结构锁),Waiting for table level lock是存储引擎层的表锁,二者日志提示不同,处理方式也不同
为什么 5.7 特别容易踩这个坑?
MySQL 5.7 默认仍允许显式 LOCK TABLES,且对 MyISAM 表支持完整,同时它的统计信息和索引选择策略不如 8.0 成熟 —— 比如一个本该走索引的 WHERE col = ? 查询,在 5.7 中可能因统计偏差走全表扫描,进而引发表级锁效应。
更隐蔽的是:某些 ORM 框架(如旧版 Django 或 Laravel)在执行批量更新或迁移时,会悄悄调用 LOCK TABLES,而开发者根本没意识到。
真正麻烦的不是锁本身,而是它不报错、不超时、不自动释放 —— 请求只是安静地排队,直到连接超时或 DBA 手动干预。











