Record Lock只能加在索引上,因为InnoDB的锁机制与B+Tree索引深度耦合,“锁一行”本质是锁索引叶子节点的索引项;无索引时需全表扫描并逐行加锁,等效表锁,引发高冲突、高开销与死锁风险。

Record Lock 为什么只能加在索引上,不能直接锁数据行?
因为 InnoDB 没有“直接锁数据行”的能力——它的锁机制和数据定位完全耦合在 B+Tree 索引结构里。所谓“锁一行”,本质是“锁住索引树中某个叶子节点上的索引项”。没有索引,就等于没有快速定位路径,InnoDB 只能退回到逐行扫描 + 逐行加锁,效果等同于表锁。
没索引时 Record Lock 会怎样?
显式语句如 SELECT ... FOR UPDATE 或 UPDATE ... WHERE col = ? 仍会执行加锁逻辑,但因 EXPLAIN 显示 type=ALL、key=NULL,InnoDB 无法跳过无关行,只能对**全表扫描过程中访问到的每一行**都加上 record lock。这导致:
- 并发更新同一张表的不同行,也会互相阻塞(比如事务 A 更新 phone='138',事务 B 更新 phone='139',但都没索引 → 全表扫,锁重叠)
- 锁数量爆炸,内存开销大,且极易触发死锁检测超时
- 性能表现和表锁无异,却还带着行锁的管理成本
唯一索引 vs 普通索引,Record Lock 范围有何差异?
锁范围取决于索引类型和查询方式,直接影响并发安全边界:
- 主键或唯一索引等值查询(如
WHERE id = 5)→ 只锁对应那一条索引记录(record lock),不带间隙 - 普通索引等值查询(如
WHERE status = 1)→ 锁该二级索引记录 + 对应的主键记录(回表需要),可能涉及多条主键锁 - 普通索引范围查询(如
WHERE created_at > '2025-01-01')→ 触发next-key lock,锁住索引区间,不只是匹配行
为什么 MyISAM 根本不支持 Record Lock?
不是它“不想”,而是物理结构不允许:MyISAM 的索引文件(.MYI)和数据文件(.MYD)分离,索引叶子存的是数据行在 .MYD 中的偏移量。你锁住索引项,只锁住了“地址”,而另一事务完全可以通过全表扫描绕过该索引,直接改写那个物理地址上的数据——锁失效。所以它只能选择最保守的方式:table lock。
真正容易被忽略的一点是:即使你写了 WHERE 条件,只要优化器没走索引,Record Lock 就形同虚设;而是否走索引,不看字段有没有建索引,而看 WHERE 表达式能否被索引覆盖、是否发生隐式类型转换、是否用了函数包装等细节。


















