MySQL多索引场景下,行锁会锁定哪些索引的B+树叶子节点?
MySQL行锁与索引锁定的核心问题解析
1. InnoDB行锁会锁定哪些索引的B+树叶子节点?
InnoDB的行锁是基于索引触发的,锁定范围取决于SQL的访问路径和操作类型:
- 若SQL通过聚簇索引
ind_a访问数据:- 直接锁定
ind_a对应叶子节点的行记录; - 如果操作会修改二级索引
ind_b的键值(比如更新了b字段),则会额外锁定ind_b中对应记录的叶子节点;若不修改b字段,不会锁定ind_b。
- 直接锁定
- 若SQL通过二级索引
ind_b访问数据:- 首先锁定
ind_b对应叶子节点的行记录; - 由于二级索引叶子节点只存主键,必须回表到聚簇索引
ind_a,因此会同时锁定ind_a中对应记录的叶子节点; - 如果操作修改了
ind_b的键值,后续还会处理二级索引的变更,但锁是在访问阶段就加上的。
- 首先锁定
2. 是否会同时锁定聚簇索引和二级索引?
不一定,分场景:
- 当通过二级索引访问并回表、或更新操作涉及修改二级索引键值时,会同时锁定两者;
- 仅通过聚簇索引访问且不修改任何二级索引键值时,只会锁定聚簇索引的叶子节点。
3. 大量索引存在时,性能会下降吗?
会的。原因有两点:
- 每加一把锁都需要占用内存资源,且锁的申请、释放、冲突检查都会带来CPU开销;
- 如果更新操作涉及多个二级索引的键值修改,InnoDB需要逐个锁定这些二级索引的对应节点,会增加锁等待的概率,高并发场景下性能衰减更明显。
4. 仅锁定单个索引会破坏事务原子性吗?
会。举个并发更新的例子:
- 事务1通过
ind_a锁定聚簇索引行,准备更新该行数据; - 事务2通过
ind_b锁定二级索引行,准备更新同一行;
此时两个事务各自持有部分锁,互相等待对方释放,不仅会触发死锁,更严重的是如果没有统一锁定所有相关索引,可能出现一个事务修改了聚簇索引、另一个修改了二级索引的情况,导致索引数据不一致,直接破坏事务的原子性(事务的更新必须全量生效,不能只修改部分索引)。
关于你提出的锁顺序方案的补充
你提到的「先锁查询用的索引,再锁受影响的二级索引,最后锁聚簇索引」的思路,确实会面临死锁风险——比如事务1按ind_a→ind_b的顺序加锁,事务2按ind_b→ind_a的顺序加锁,就会触发循环等待导致死锁。
InnoDB实际的处理逻辑是按需加锁+统一锁顺序:比如通过二级索引访问时,强制先加二级索引锁,再加聚簇索引锁,以此减少死锁概率;同时内置死锁检测机制,定期扫描锁等待队列,发现死锁后回滚代价较小的事务,避免无限等待。
内容的提问来源于stack exchange,提问作者Name Null
相关产品推荐
相关产品推荐

