为什么MySQL InnoDB的SELECT FOR UPDATE几乎锁定了整张表?
问题根因
你遇到的全表锁现象本质是优化器选择了全表扫描而非主键范围扫描导致的,和InnoDB的加锁逻辑本身并不冲突。
测试表仅4条数据,优化器计算执行成本时,会判定全表扫描的IO成本远低于走主键索引的成本,最终执行计划为全表扫描。
而在RR隔离级别下,InnoDB对于全表扫描的UPDATE/SELECT FOR UPDATE语句,会给表中所有记录加X型Next-Key Lock,同时覆盖所有记录之间的间隙,相当于锁定了整张表的所有插入可能,所以你测试的所有插入操作都会被阻塞。
验证方案
你可以执行下面的语句查看执行计划确认:
EXPLAIN SELECT * FROM child WHERE id >= 102 for update;
如果输出的type列值为ALL,就说明确实走了全表扫描。
你可以通过两种方式验证加锁逻辑符合官方描述:
- 强制走主键索引:
SELECT * FROM child FORCE INDEX(PRIMARY) WHERE id >= 102 FOR UPDATE;
此时再查询performance_schema.data_locks,就能看到只有你预期的3个Next-Key Lock,插入小于90的id不会被阻塞。
2. 增加测试表数据量,比如插入几百条id不重复的记录,优化器就会自动选择主键范围扫描,加锁范围也会符合预期。
补充说明
你对官方文档的理解是完全正确的:InnoDB只会对实际扫描到的索引范围加锁。出现不符合预期的加锁范围时,优先检查执行计划是否和预期一致,绝大多数场景下都是优化器的执行计划选择导致的。
内容的提问来源于stack exchange,提问作者xhinliang
相关产品推荐
相关产品推荐

