【问题标题】:Are double reads a possibility in InnoDB在 InnoDB 中是否可以进行双重读取
【发布时间】:2015-02-21 10:53:26
【问题描述】:

https://msdn.microsoft.com/en-us/en-en/library/ms190805.aspx 中提到的称为“行更新导致的缺失和双重读取”的并发效应是否与 Innodb 引擎相关?

例如:

在 READ UNCOMMITTED 级别运行的事务不会发出共享锁以防止其他事务修改当前事务读取的数据。在 READ COMMITTED 级别运行的事务确实会发出共享锁,但行或页锁在读取行后会被释放。在任何一种情况下,当您扫描索引时,如果另一个用户在您读取期间更改了该行的索引键列,则如果键更改将该行移动到扫描之前的位置,则该行可能会再次出现。同样,如果键更改将行移动到索引中您已读取的位置,则该行可能不会出现。为避免这种情况,请使用 SERIALIZABLE 或 HOLDLOCK 提示,或行版本控制

还有一个更新。从 MSSQL 引擎: "Microsoft SQL Server 2008 内部"

在某些情况下,扫描最终可能会返回多次出现的行,甚至会跳过行。分配顺序扫描比索引顺序扫描更容易出现这种行为。我将首先描述分配顺序扫描如何发生这种现象以及在何种情况下会发生这种现象。然后我将解释如何在索引顺序扫描中发生这种情况。分配顺序扫描 图 4-30 分三个步骤演示了分配顺序扫描如何返回多次出现的行。第 1 步显示正在进行的分配顺序扫描,按文件顺序(不是索引顺序)读取某些索引的叶页。已经阅读了两页(键 50、60、70、80、10、20、30、40)。此时,在读取索引的第三页之前,有人使用键 25 将一行插入到表中。步骤 2 显示了在作为插入目标的页面中发生的拆分,因为该页面已满。作为分割的结果,一个新的页面被分配了——在我们的例子中,在文件后面的一个扫描还没有到达的点上。来自原始页面的一半行移动到新页面(键 30、40),并且具有键 25 的新行由于其键值而被添加到原始页面。步骤 3 显示了扫描的继续:读取剩余的两页(键 90、100、110、120、30、40),包括由于拆分而​​添加的那一页。请注意,键为 30 和 40 的行被第二次读取。

【问题讨论】:

  • 还有一个更新。来自 MSSQL 引擎:

标签: mysql database innodb


【解决方案1】:

这在某些情况下可能与 InnoDB 引擎有关。

对于InnoDB,在READ COMMITTEDREPEATABLE READ 事务隔离级别发出SELECT 查询时,使用Consistent Read 模式,这是MVCC 的实现,也称为乐观并发。在这种模式下,读取查询不会发出任何锁,而是引擎会维护数据库的快照,就像查询开始时一样。在提交之前(或整个事务,取决于选择了上述两个隔离级别中的哪一个),它不会看到该查询之外的任何更改。

这种情况是你在问题中描述的情况是不可能的吗?

上面提到的 MySQL 手册部分的示例:

        Session A               Session B

        SET autocommit=0;       SET autocommit=0;
time
|       SELECT * FROM t;
|       empty set
|                               INSERT INTO t VALUES (1, 2);
|
v       SELECT * FROM t;
        empty set
                                COMMIT;

        SELECT * FROM t;
        empty set

        COMMIT;

        SELECT * FROM t;
        ---------------------
        |   1   |   2   |
        ---------------------
        1 row in set

READ UNCOMMITTED 事务隔离级别下发出的读取查询绕过 MVCC 并“查看”数据库中发生的一切,包括任何未提交的事务。这使得 phantomdirty reads 成为一个问题。

读取依赖于显式使用锁(SELECT... FOR UPDATESELECT... LOCK IN SHARED MODE)的查询或在SERIALIZABLE 隔离级别下的查询会自动回退到基于锁的并发。后者将任何SELECT 查询升级到LOCK IN SHARED MODE。在这种特殊情况下,您是否可以避免问题中描述的数据移动取决于您使用的SELECT 查询WHERE 谓词。这会影响引擎是仅锁定您刚刚读取的数据还是锁定您已读取的数据之间的整个范围。以下是the relevant manual page的摘录:

对于锁定读取(SELECT with FOR UPDATE 或 LOCK IN SHARE MODE)、UPDATE 和 DELETE 语句,锁定取决于语句是使用具有唯一搜索条件的唯一索引还是范围类型的搜索条件。对于具有唯一搜索条件的唯一索引,InnoDB 只锁定找到的索引记录,而不锁定它之前的间隙。对于其他搜索条件,InnoDB 锁定扫描的索引范围,使用间隙锁或下一个键(间隙加索引记录)锁来阻止其他会话插入范围所覆盖的间隙。

【讨论】:

  • 所以这是不可能的,对吧?:如果另一个用户在您阅读期间更改了该行的索引键列,如果键更改将该行移动到前面的位置,则该行可能会再次出现您的扫描
  • @temper 我已经更深入地审查了这个主题并完全重写了答案。如果您需要更多详细信息,请联系我们。
  • 更新了问题,因为索引的情况也会影响结果。
  • 是的,我想说这同样适用于InnoDB,只要您不在 MVCC 一致读取 模式下工作,即在有或没有基础表的情况下都会发生物理锁定具有 B 树索引,如答案中所述。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-30
  • 2012-11-16
  • 2011-05-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多