在 InnoDB 中是否有可能进行双重读取

Are double reads a possibility in InnoDB

称为 "Missing and Double Reads Caused by Row Updates" 并在此处提到 https://msdn.microsoft.com/en-us/en-en/library/ms190805.aspx 的并发效果是否与 Innodb 引擎相关?

例如:

Transactions that are running at the READ UNCOMMITTED level do not issue shared locks to prevent other transactions from modifying data read by the current transaction. Transactions that are running at the READ COMMITTED level do issue shared locks, but the row or page locks are released after the row is read. In either case, when you are scanning an index, if another user changes the index key column of the row during your read, the row might appear again if the key change moved the row to a position ahead of your scan. Similarly, the row might not appear if the key change moved the row to a position in the index that you had already read. To avoid this, use the SERIALIZABLE or HOLDLOCK hint, or row versioning

还有一个更新。来自 MSSQL 引擎: “深入了解 Microsoft SQL Server 2008

In certain circumstances, scans can end up returning multiple occurrences of rows or even skip rows. Allocation order scans are more prone to such behavior than index order scans. I’ll fi rst describe how such a phenomenon can happen with allocation order scans and in which circumstances. Then I’ll explain how it can happen with index order scans. Allocation Order Scans Figure 4-30 demonstrate in three steps how an allocation order scan can return multiple occurrences of rows. Step 1 shows an allocation order scan in progress, reading the leaf pages of some index in fi le order (not index order). Two pages were already read (keys 50, 60, 70, 80, 10, 20, 30, 40). At this point, before the third page of the index is read, someone inserts a row into the table with key 25. Step 2 shows a split that took place in the page that was the target for the insert since it was full. As a result of the split, a new page was allocated—in our case later in the fi le at a point that the scan did not yet reach. Half the rows from the original page move to the new page (keys 30, 40), and the new row with key 25 was added to the original page because of its key value. Step 3 shows the continuation of the scan: reading the remaining two pages (keys 90, 100, 110, 120, 30, 40) including the one that was added because of the split. Notice that the rows with keys 30 and 40 were read a second time..

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

对于 InnoDBSELECT 查询在 READ COMMITTEDREPEATABLE READ 事务隔离级别中发出 Consistent Read mode is used, which is an implementation of 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 和 "see" 数据库中发生的一切,包括任何未提交的事务。这使得 幻像 脏读 成为一个问题。

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

For locking reads (SELECT with FOR UPDATE or LOCK IN SHARE MODE), UPDATE, and DELETE statements, locking depends on whether the statement uses a unique index with a unique search condition, or a range-type search condition. For a unique index with a unique search condition, InnoDB locks only the index record found, not the gap before it. For other search conditions, InnoDB locks the index range scanned, using gap locks or next-key (gap plus index-record) locks to block insertions by other sessions into the gaps covered by the range.