欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

14.5.2.3 一致性非阻塞读

程序员文章站 2022-06-14 18:57:11
...

原文:https://dev.mysql.com/doc/refman/5.7/en/innodb-consistent-read.html

14.5.2.3 Consistent Nonlocking Reads

一致性读意味着InnoDB引擎使用多版本控制(multi-versioning)提供查询在某一时间点快照的能力。这个查询能够看到在这个时间点之前提交的数据,但是不能看到其它事务之后的更改和尚未提交的更改。例外情况:查询语句能够看到同一个事务在查询语句执行前操作的数据。此特例导致下面一个异常情况:当你更新了表中的某些行,select语句就能够看到被更新数据的最新版本。如果其它session同时更新了同一个表,这时你可能看到表处于数据库未定义的状态。

如果事务的隔离级别是REPEATABLE READ (默认级别),同一个事务的所有的一致性读都是从第一次read时建立的快照版本中读取的。commit当前事务然后在查询就会得到更新一些的快照。

在READ COMMITTED隔离级别下,每一个一致性读都会刷新快照并从快照中读取数据。

一致性读是InnoDB在READ COMMITTED and REPEATABLE READ下默认的查询模式,一致性读不会对访问的表加任何锁,因此其它事务的修改表操作可以和查询动作并行执行。

假设你是在REPEATABLE READ下运行,当发生一致性读操作时(也就是说,执行了一条普通的查询语句),InnoDB引擎会给的事务一个”时间点”,该时间点决定了事务看到的数据范围。如果在该时间点之后另外一个事务删除了一行数据并提交了事务,你不会看到这行数据已经被删除。insert和update也是类似的处理方式。

注意
数据库对SELECT提供了快照查询功能,但是快照对DML语句并不一定有效。如果你在一个事务中insert或修改某些数据然后commit,另外一个事务的即使在REPEATABLE READ级别下执行update或delete语句也可能对刚才提交的数据产生影响,即使该事务在快照中查找不到这些数据。
一旦一个事务update 或 delete 了其它事务提交的数据,那么当前会看到这些数据,并且是最新版本。你可能遇到过下面的情况:

  ``` sql
      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.
```

你可以通过提交当前事务,然后再执行select语句或重新开启一个事务执行查询,以便更新“时间点”。

这就是多版本并发控制(multi-versioned concurrency control)。

下面的例子中,session A只有在session B提交了insert数据并且A也commit后,才能看到B插入的数据。这样session A的时间点才能在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 LOCK IN SHARE MODE;

在READ COMMITTED隔离级别下,每个一致性读事务都会刷新快照然后在从快照中读取。在LOCK IN SHARE下,一个读锁替代快照:一个查询语句将会被阻塞直到它获取最新的数据。

一致性读在一些DDL语句中不起效:

  • 一致性读不会在DROP TABLE语句下工作,因为MySQL无法使用已经drop的表。

  • 一致性读不会在ALTER TABLE语句下工作,因为这个语句会生成原始表的临时备份,一旦备份构建成功原始表将会被删除。如果此时用一致性读,会查不到数据。因为表中并不存在该事务时间点之前的数据。在这种情况下,会产生一个错误:ER_TABLE_DEF_CHANGED(table 的定义已经变更,请重试该事务)。

    译者注:在MySQL5.7 RR下实验表明:只要开启了事务并在表中执行了语句(select,update or delete),只要没有commit,drop table,alter table就会被阻塞。

一些select语句,像INSERT INTO … SELECT, UPDATE … (SELECT), and CREATE TABLE … SELECT ,这些没有声明FOR UPDATE or LOCK IN SHARE MODE,使用一致性读的情况如下:

  • 默认情况下,InnoDB会使用较粗粒度的锁,select部分与在READ COMMITTED模式下执行相同:即使是同一个事务,也会刷新快照。

  • 如果打开了innodb_locks_unsafe_for_binlog选项在READ UNCOMMITTED,READ COMMITTED, or REPEATABLE READ (除了SERIALIZABLE以外的级别),在这种情况下,对查询的表不会加锁。

相关标签: 事务 mysql mvcc