MySQL表锁在LOCK TABLES、DDL操作、MyISAM引擎DML及特定语句时触发;MyISAM强制表锁,DDL需MDL锁,手动加锁易引发事务提交与并发问题,生产中应优先用唯一索引而非表锁。

表锁在 MySQL 中的触发时机
MySQL 默认使用行级锁(InnoDB),但表锁会在以下明确场景被强制启用:LOCK TABLES 手动加锁、执行 ALTER TABLE 等 DDL 操作、MyISAM 引擎的所有 DML 操作,以及某些特殊语句(如 INSERT ... SELECT 在非事务引擎下)。
注意:InnoDB 通常避免表锁,但当优化器判断全表扫描 + 锁行开销过大时,可能退化为 LOCK TABLES ... WRITE —— 这类行为不可控,且只发生在极少数旧版本或配置不当的实例中。
MyISAM 表锁是唯一选择
MyISAM 不支持事务和行锁,所有 UPDATE、DELETE、INSERT 都自动加表级写锁,SELECT 加表级读锁。这意味着:
- 并发写入完全串行化,高写负载下性能急剧下降
-
CONCURRENT INSERT是唯一例外:当表尾无空洞时,INSERT可与SELECT并发,但依然阻塞其他UPDATE/DELETE - 无法通过索引优化规避表锁 —— 锁粒度就是整张表
DDL 操作必然触发元数据锁(MDL)
从 MySQL 5.5 开始,所有 DDL(如 ALTER TABLE、DROP INDEX)会持有 MDL 锁,本质是表级锁的一种。它不阻塞查询,但会阻塞:
- 后续对该表的 DDL(如两个
ALTER不能并行) - 长事务中的 DML:如果一个事务已开启但未提交,且访问过该表,那么
ALTER会被挂起,直到事务结束 -
FLUSH TABLES WITH READ LOCK也会申请全局表锁,常用于物理备份,但会使整个库只读
手动加表锁的典型误用场景
LOCK TABLES t1 WRITE 看似可控,实际极易引发问题:
- 仅对当前连接有效,其他连接仍可读(除非显式
READ LOCAL),但写操作全部阻塞 - 必须用
UNLOCK TABLES显式释放 —— 连接断开会自动释放,但程序异常未调用会导致锁残留 - 与事务冲突:InnoDB 中若已开启事务,再执行
LOCK TABLES会隐式提交当前事务,破坏原子性 - 无法和行锁共存:一旦加了表锁,InnoDB 就不再使用行锁机制,彻底退化为 MyISAM 式并发模型
真正需要表锁的生产场景极少,多数情况是误判了锁粒度需求 —— 比如想“防止并发插入重复数据”,正确解法是加唯一索引+处理 Duplicate entry 错误,而非锁表。










