mysql innodb行锁不会升级为表锁,真实情况是:无索引时全表扫描并逐行加record lock或next-key lock,锁大量行导致效果等同表锁,但底层仍是行级锁。

MySQL InnoDB 的行锁不会主动“升级”为表锁——这是个常见误解。真实情况是:当查询无法走索引时,InnoDB 被迫全表扫描,对每行聚簇索引记录加 Record Lock(或 Next-Key Lock),锁了成千上万行,效果等同于表锁,但底层仍是行级锁。
WHERE 条件没走索引,就等于锁全表
只要 EXPLAIN 显示 type = ALL 或 key = NULL,说明没命中索引,InnoDB 就会遍历所有聚簇索引页,逐行加锁。
- 哪怕只更新 1 行,比如
UPDATE users SET status=1 WHERE name='alice',而name没索引 → 锁全部主键记录 - 在可重复读(RR)隔离级别下,还会额外加
Gap Lock,锁住所有间隙,范围进一步扩大 -
SELECT * FROM orders WHERE created_at > '2024-01-01' FOR UPDATE,若created_at无索引,同样锁全表
怎么确认是真表锁还是“伪表锁”
别信 SHOW ENGINE INNODB STATUS\G 里模糊的 “TABLE LOCK” 提示——它只显示最近事务,漏掉静默持有者。真正可靠的判断方式是查 performance_schema.data_locks:
-
LOCK_TYPE = 'TABLE'→ 真表锁,比如LOCK TABLES t WRITE、ALTER TABLE、TRUNCATE -
LOCK_TYPE = 'RECORD'+INDEX_NAME = 'PRIMARY'+ 锁行数远超预期 → 就是“伪表锁”,本质是海量行锁堆积 - 配合
performance_schema.data_lock_waits查谁在等谁,比旧版INNODB_LOCK_WAITS更准
哪些操作根本不是锁升级,而是独立机制
有些行为看起来像“行锁变表锁”,其实和锁升级完全无关,属于其他机制触发的真表级阻塞:
-
LOCK TABLES t WRITE:会话级独占锁,后续所有 DML 直接被拦,SELECT ... FOR UPDATE会报ERROR 1100 -
ALTER TABLE/TRUNCATE TABLE:由 Server 层元数据锁(MDL)控制,阻塞所有并发 DML - MyISAM 表任意写操作:该引擎压根不支持行锁,
UPDATE就是表锁 - 未命中主键/唯一索引的
INSERT:InnoDB 对自增段加锁,高并发下表现接近表锁,但仍是索引粒度
真正要盯住的,永远是那条语句有没有走索引——锁范围宽窄,不在锁本身,而在执行计划。哪怕加了索引,也要防隐式类型转换、函数包装、LIKE '%abc' 这类让索引失效的操作。











