14.5.2.3 一致性非阻塞读

来源:互联网 发布:百利进销存软件网络版 编辑:程序博客网 时间:2024/05/19 05:29

原文: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以外的级别),在这种情况下,对查询的表不会加锁。

原创粉丝点击