为什么MySQL在搜索索引列时不锁定行



例如,我锁定了一些行:

select * from t1 where c2 = 1 for update;

c2没有被索引。这意味着MySQL必须搜索整个表,如果它读取了未提交或已提交的隔离级别,它会在扫描的每一行上加锁,如果不满足WHERE条件,它会立即释放锁。

如果它是可重复读取的,那么那些不满足WHERE条件的锁将一直保留到事务结束。

当MySQL出于某种原因搜索索引列时,它不会对不满足WHERE条件的行进行锁定。是的,它使用了另一种算法,允许它在3-4次提取中找到行,但在找到正确的行之前,它仍然会接触一些行。

实际上,真正的问题不是为什么MySQL在使用索引时不锁定行,而是为什么它在不使用索引时锁定这些行。

简化一点(取决于您的隔离级别(,为您的查询发布的锁应该防止两件事:

  • 具有c=1的行不应被其他事务更改,例如更改为c=2,因为它将不再满足您的原始条件

  • 带有c=2的行不应更改为c=1(并且不应插入带有c=1的新行(,因为它现在将满足您的原始条件(因此,如果其他事务先出现,它将由您的查询选择(

第一个bulletpoint的锁定基本上只需要用c=1锁定行。索引和未索引的情况之间没有根本区别:当前具有c=1的行最终将被锁定。

然而,对于第二个关键点来说,这更为棘手:

不幸的是,对于未编制索引的情况,MySQL无法区分另一个事务是将c=2的行更改为c=5(这很好(,还是将c=2的行更改至c=1(必须防止(。由于锁定太多行总比不够好,MySQL只会这样做:锁定所有行。这确保了它防止了c=2c=1的修改。其他行上的锁是抵押品,这是为了获得更大的利益(或不添加索引(而必须支付的价格。

MySQL将保持这个";过度保护行为;up:为了防止用c=1插入新行,它基本上会通过锁定所有行(和间隙,我在这里不详细介绍(来防止任何插入(到主键中(。

对于索引情况,MySQL有另一个选项:如果修改想要将c=2的行更改为c=1,则需要该行在索引的c=1区域中获得一个新条目。因此MySQL可以";只是";在那里放置锁以防止对c=1处的索引进行注入(插入或更新(。基本上,为了安全起见,它不必预先锁定所有行,但事后可以检测到不允许的修改。

最新更新