文档主页
MySQL 9.0 参考手册
相关文档 下载本手册
PDF (US Ltr) - 40.0Mb
PDF (A4) - 40.1Mb
手册页 (TGZ) - 258.2Kb
手册页 (Zip) - 365.3Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


17.7.2.3 一致性非锁定读取

一致性读取意味着 InnoDB 使用多版本来向查询呈现某个时间点的数据快照。查询将看到该时间点之前提交的事务所做的更改,而不会看到之后或未提交的事务所做的更改。此规则的例外情况是,查询会看到同一事务中较早语句所做的更改。这种例外情况会导致以下异常:如果在表中更新了一些行,那么 SELECT 将看到已更新行的最新版本,但它也可能看到任何行的旧版本。如果其他会话同时更新同一表,则异常意味着您可能看到表处于数据库中从未存在过的状态。

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

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

一致性读取是 InnoDBREAD COMMITTEDREPEATABLE READ 隔离级别中处理 SELECT 语句的默认模式。一致性读取不会在它访问的表上设置任何锁,因此其他会话可以在对表执行一致性读取时同时修改这些表。

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

注意

数据库状态的快照适用于事务中的 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;

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

一致性读取不适用于某些 DDL 语句

  • 一致性读取不适用于DROP TABLE,因为 MySQL 无法使用已删除的表,并且InnoDB会销毁该表。

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

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

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

  • 要在这种情况下执行非锁定读取,请将事务的隔离级别设置为READ UNCOMMITTEDREAD COMMITTED,以避免对从所选表读取的行设置锁。