为什么 MySQL 在搜索索引列时不锁定行
Why does MySQL not lock rows while searching indexed column
比如我锁定了一些行:
select * from t1 where c2 = 1 for update;
c2 未编入索引。这意味着 MySQL 必须搜索整个 table,如果它读取未提交或读取已提交的隔离级别,它会在每个扫描的行上放置锁,如果它不满足 WHERE 条件,它会立即释放锁。
如果是repeatable读取那些不满足WHERE条件的锁一直保留到事务结束
当 MySQL 出于某种原因搜索索引列时,它不会锁定不满足 WHERE 条件的行。是的,它使用另一种算法,允许它在 3-4 次提取中找到行,但在找到正确的行之前它仍然会接触到一些行。
实际上,真正的问题不是为什么 MySQL 在使用索引时不锁定行,而是为什么它在 不 使用索引时锁定这些行.
稍微简化一下(并取决于您的隔离级别),为您的查询发出的锁可以防止两件事:
具有 c=1
的行不应由不同的事务更改,例如c=2
,因为它不再满足您的原始条件
不应将带有 c=2
的行更改为 c=1
(并且不应插入带有 c=1
的新行),因为它现在可以满足您的需求原始条件(因此如果其他交易先出现,您的查询就会选择它)
锁定第一个要点基本上只需要使用 c=1
锁定行。索引和未索引情况之间没有根本区别:当前具有 c=1
的行最终将被锁定。
然而,对于第二个要点,那就更棘手了:
对于未索引的情况,不幸的是,MySQL 无法区分另一个事务是将 c=2
的行更改为 c=5
(这没问题)还是c=2
到 c=1
(必须避免)。由于锁定太多行总比锁定不够好,因此 MySQL 会这样做:锁定所有行。这确保它可以防止将 c=2
修改为 c=1
。其他行上的锁是抵押品,您必须为更大的利益(或不添加索引)付出代价。
MySQL 将保持这种“过度保护行为”:为了防止使用 c=1
插入新行,它基本上会阻止 any 插入(进入主键)通过,嗯,锁定所有行(和间隙,我不会在这里详细介绍)。
对于索引情况,MySQL 有另一个选择:如果修改想要将 c=2
的行更改为 c=1
,则需要该行获得一个新条目在索引的 c=1
区域。因此 MySQL 可以“只是”在此处放置一个锁以防止注入(插入或更新)到 c=1
处的索引中。基本上,它不必为了安全而先发制人地锁定所有行,但可以在事后检测到不允许的修改。
比如我锁定了一些行:
select * from t1 where c2 = 1 for update;
c2 未编入索引。这意味着 MySQL 必须搜索整个 table,如果它读取未提交或读取已提交的隔离级别,它会在每个扫描的行上放置锁,如果它不满足 WHERE 条件,它会立即释放锁。
如果是repeatable读取那些不满足WHERE条件的锁一直保留到事务结束
当 MySQL 出于某种原因搜索索引列时,它不会锁定不满足 WHERE 条件的行。是的,它使用另一种算法,允许它在 3-4 次提取中找到行,但在找到正确的行之前它仍然会接触到一些行。
实际上,真正的问题不是为什么 MySQL 在使用索引时不锁定行,而是为什么它在 不 使用索引时锁定这些行.
稍微简化一下(并取决于您的隔离级别),为您的查询发出的锁可以防止两件事:
具有
c=1
的行不应由不同的事务更改,例如c=2
,因为它不再满足您的原始条件不应将带有
c=2
的行更改为c=1
(并且不应插入带有c=1
的新行),因为它现在可以满足您的需求原始条件(因此如果其他交易先出现,您的查询就会选择它)
锁定第一个要点基本上只需要使用 c=1
锁定行。索引和未索引情况之间没有根本区别:当前具有 c=1
的行最终将被锁定。
然而,对于第二个要点,那就更棘手了:
对于未索引的情况,不幸的是,MySQL 无法区分另一个事务是将 c=2
的行更改为 c=5
(这没问题)还是c=2
到 c=1
(必须避免)。由于锁定太多行总比锁定不够好,因此 MySQL 会这样做:锁定所有行。这确保它可以防止将 c=2
修改为 c=1
。其他行上的锁是抵押品,您必须为更大的利益(或不添加索引)付出代价。
MySQL 将保持这种“过度保护行为”:为了防止使用 c=1
插入新行,它基本上会阻止 any 插入(进入主键)通过,嗯,锁定所有行(和间隙,我不会在这里详细介绍)。
对于索引情况,MySQL 有另一个选择:如果修改想要将 c=2
的行更改为 c=1
,则需要该行获得一个新条目在索引的 c=1
区域。因此 MySQL 可以“只是”在此处放置一个锁以防止注入(插入或更新)到 c=1
处的索引中。基本上,它不必为了安全而先发制人地锁定所有行,但可以在事后检测到不允许的修改。