Documentation Home

15.7.2.3 一致的非锁定读取

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

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

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

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

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

笔记

数据库状态的快照适用 SELECT于事务中的语句,不一定适用于 DML语句。如果您插入或修改某些行然后提交该事务, 则从另一个并发事务发出的DELETEor 语句 可能会影响那些刚刚提交的行,即使会话无法查询它们。如果一个事务确实更新或删除了由另一个事务提交的行,那么这些更改对当前事务是可见的。例如,您可能会遇到如下情况: UPDATEREPEATABLE READ

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.

您可以通过提交事务然后执行另一个SELECTor 来提前时间点START 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,改为发生锁定读取:ASELECT阻塞,直到包含最新行的事务结束(请参阅 第 15.7.2.4 节,“锁定读取”)。

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

  • Consistent read 不起作用DROP TABLE,因为 MySQL 不能使用已被删除的表并InnoDB破坏该表。

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

对于子句中的选择,读取的类型会有所不同,例如 INSERT INTO ... SELECT, UPDATE ... (SELECT), 并且 CREATE TABLE ... SELECT没有指定FOR UPDATEor FOR SHARE

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

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