mysql中只有一个索引列,为什么用range条件锁定read会锁定每条记录?

piv4azn7  于 2023-01-29  发布在  Mysql
关注(0)|答案(1)|浏览(165)

创建两个表格进行比较:

create table t1(id integer primary key);
create table t2(id integer primary key, num integer);
insert into t1 values (0),(3),(6),(9);
insert into t2 values (0, 0), (3, 3), (6, 6), (9, 9);

启动事务tx1并对表t1执行锁定读取:

start transaction;
select * from t1 where id >=3 and id < 8 for update;

tx1持有的锁如下所示:

现在回滚tx1,启动事务tx2并对表t2执行锁定读取:

start transaction;
select * from t2 where id >=3 and id < 8 for update;

tx2持有的锁如下所示:

tx2的行为符合我对mysql锁定模型的理解,为什么tx1要锁定表t1中的每个索引记录?
===============更新================
现在使用explain,我看到了一些不同之处:

===============更新2 ================
正如@Bill Karwin的回答所指出的,type: index意味着扫描整个索引树,参见ref连接类型。
但是,如果我再插入一行:insert into t1 values (12),则连接类型更改为type: range,并且不会锁定所有索引记录!

我猜mysql源代码中有一些内部怪癖。

63lcw9qa

63lcw9qa1#

检查的所有行都被锁定。
答案在EXPLAIN报告中。执行type: index的查询是在执行索引扫描,它检查索引的每个成员。对于PRIMARY索引,这实际上检查了整个表。
执行type: range的查询正在检查行的子集。
优化器出于某种原因选择进行索引扫描。我不确定确切的原因。这可能取决于您使用的MySQL版本。您可以使用以下查询进行检查:SELECT VERSION();
我相信当这个查询是一个覆盖索引时,它被当作一个索引扫描来处理。这就是解释注解“使用索引”。如果查询获取的列与索引中的列相同,那么它就是一个覆盖索引。
如果测试以下查询,您将获得相同的优化策略(索引扫描):

select id from t2 where id >=3 and id < 8 for update;

因为id是主键索引的列,并且是查询返回的唯一列。

相关问题