Documentation Home
MySQL 8.3 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 40.8Mb
PDF (A4) - 40.9Mb
Man Pages (TGZ) - 294.0Kb
Man Pages (Zip) - 409.0Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb
Excerpts from this Manual

17.7.2.3 一致的非锁定读取

一致的读取意味着 InnoDB 使用多版本控制来呈现查询的数据库快照在某个时间点。该查询看到在该时间点之前提交的所有事务的更改,并且不看到后续或未提交的事务的更改。该规则的一个例外是,该查询看到同一事务中早期语句的更改。这导致以下异常:如果您更新表中的某些行,一个 SELECT 看到最新版本的更新行,但它也可能看到其他行的旧版本。如果其他会话同时更新同一表,该异常意味着您可能看到表处于从未存在于数据库中的状态。

如果事务 隔离级别REPEATABLE READ(默认级别),同一事务中的所有一致读取都读取该事务中第一个一致读取所建立的快照。您可以通过提交当前事务,然后发出新查询来获取最新的快照。

使用 READ COMMITTED 隔离级别,每个一致读取在事务中设置并读取其自己的最新快照。

一致读取是 InnoDB 处理 SELECT 语句的默认模式,在 READ COMMITTEDREPEATABLE READ 隔离级别下。一个一致读取不设置表的锁,因此其他会话可以在同一时间修改这些表,而一致读取正在执行。

假设您正在运行默认的 REPEATABLE READ 隔离级别。当您发出一致读取(即普通的 SELECT 语句)时,InnoDB 根据您的时间点为您的交易分配快照。如果另一个事务删除了一行并在您的时间点之后提交,您不会看到该行被删除。插入和更新以类似的方式处理。

Note

数据库状态的快照适用于 SELECT 语句在事务中,而不是 DML 语句。如果您插入或修改某些行,然后提交该事务,来自其他并发 REPEATABLE READ 事务的 DELETEUPDATE 语句可能会影响刚刚提交的行,即使该会话无法查询它们。如果事务更新或删除了其他事务提交的行,那么这些更改将变得可见于当前事务。例如,您可能会遇到以下情况:

SELECT COUNT(c1) FROM t1 WHERE c1 = 'xyz';
-- Returns 0: no rows match.
DELETE FROM t1 WHERE c1 = 'xyz';
-- Deletes several rows recently committed by other transaction.

SELECT COUNT(c2) FROM t1 WHERE c2 = 'abc';
-- Returns 0: no rows match.
UPDATE t1 SET c2 = 'cba' WHERE c2 = 'abc';
-- Affects 10 rows: another txn just committed 10 rows with 'abc' values.
SELECT COUNT(c2) FROM t1 WHERE c2 = 'cba';
-- Returns 10: this txn can now see the rows it just updated.

您可以通过提交事务,然后再次执行 SELECTSTART TRANSACTION WITH CONSISTENT SNAPSHOT 来推进时间点。

这被称为 多版本并发控制

在以下示例中,会话 A 只有在 B 提交插入并且 A 也提交后,才能看到 B 插入的行,以便时间点超越 B 的提交。

             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    |
           ---------------------

如果您想看到数据库的最新状态,请使用 READ COMMITTED 隔离级别或锁定读取:

SELECT * FROM t FOR SHARE;

已提交读取 隔离级别中,每个一致读取在事务中设置并读取其自己的最新快照。使用 FOR SHARE,锁定读取会发生:SELECT 会阻塞,直到包含最新行的事务结束(见 第 17.7.2.4 节,“锁定读取”)。

一致读取不能在某些 DDL 语句上工作:

  • 一致读取不能在 DROP TABLE 上工作,因为 MySQL 无法使用已删除的表,而 InnoDB 会销毁该表。

  • 一致读取不能在 ALTER TABLE 操作上工作,该操作会创建原始表的临时副本,并在临时副本构建完成时删除原始表。当您在事务中重新发出一致读取时,新表中的行不可见,因为这些行在事务快照拍摄时不存在。在这种情况下,事务将返回错误:ER_TABLE_DEF_CHANGED表定义已更改,请重试事务

在子句中,如 INSERT INTO ... SELECTUPDATE ... (SELECT)CREATE TABLE ... SELECT 中,不指定 FOR UPDATEFOR SHARE 的读取类型:

  • 默认情况下,InnoDB 对这些语句使用更强的锁,并且 SELECT 部分行为类似于 已提交读取,其中每个一致读取,即使在同一事务中,也会设置并读取其自己的最新快照。

  • 要在这些情况下执行非锁定读取,请将事务的隔离级别设置为 未提交读取已提交读取,以避免在选定的表上设置锁。