文档首页
MySQL 8.4 参考手册
相关文档 下载本手册
PDF (US Ltr) - 39.9Mb
PDF (A4) - 40.0Mb
手册页 (TGZ) - 258.5Kb
手册页 (Zip) - 365.5Kb
信息 (Gzip) - 4.0Mb
信息 (Zip) - 4.0Mb


17.7.2.3 一致性非锁定读取

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

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

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

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

假设您在默认的 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 SHAREINSERT INTO ... SELECTUPDATE ... (SELECT)CREATE TABLE ... SELECT 等子句中的选择,读取类型会有所不同。

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

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