InnoDB一致性非锁定读(Consistent Nonlocking Reads)

一致性读(consistend read)

一致性读(consistend read)指的是InnoDB使用多版本查询数据库在某个时间点的快照。此查询可以看到该时间点之前提交的事务所做的更改并且不会被之后的修改或者未提交事务所影响。但是对于同一事务中的较早语句的修改则不适用此规则,这种情况会产生以下异常:如果你更新表中的某些行,一次SELECT可能看到更新行的最新版本也可能看到任一行的旧版本;如果其它会话同时更新到同一个表,则可能会看到该表处于数据库中从未存在过的状态。

多版本并发控制

  • 当事务隔离级别为REPEATABLE READ时,同一个事务中的一致性读都是读取的是该事务下第一次查询所建立的快照。

我们可以通过

  • 当事务隔离级别为READ COMMITTED时,同一事务下的一致性读都会建立和读取此查询自己的最新的快照

一致性读是InnoDB在REPEATABLE READ和READ COMMITTED事务隔离中处理SELECT语句的默认模式。一致性读不会在表上设置任何锁,所以其它会话可以对表进行读写操作。

数据库状态的快照适用于事务中的SELECT语句,而不一定适用于DML语句。如果执行INSERT或者UPDATE某些行然后提交该事务,则从另一个并发REPEATABLE READ事务发出的DELETE或UPDATE语句则会影响那些刚刚提交的数据行。下面这个示例展示了这种场景:

             Session A                              Session B

           begin;                                   begin;
-------------------------------------------------------------------------------------
           SELECT * FROM t;                        SELECT * FROM t;
           > empty set                             > empty set
          
-------------------------------------------------------------------------------------
                                                   INSERT INTO t (id) VALUES (1);
                                                    > 1 row affected
          
                                                    SELECT * FROM t;
                                                    -----------
                                                    |   id    |
                                                    -----------
                                                    |    1    |
                                                    -----------
-------------------------------------------------------------------------------------            
           SELECT * FROM t;         
           > empty set
-------------------------------------------------------------------------------------                                                    
                                                    COMMIT;
-------------------------------------------------------------------------------------
           SELECT * FROM t;
           > empty set
-------------------------------------------------------------------------------------

           DELETE FROM t WAERE id = 1;
           > 1 row affected
-------------------------------------------------------------------------------------
                                                    SELECT * FROM t;
                                                    -----------
                                                    |   id    |
                                                    -----------
                                                    |    1    |
                                                    -----------
-------------------------------------------------------------------------------------
           COMMIT;
-------------------------------------------------------------------------------------
                                                    SELECT * FROM t;
                                                    > empty set
-------------------------------------------------------------------------------------
           SELECT * FROM t;
           -----------
           |   id    |
           -----------
           |    1    |
           -----------
-------------------------------------------------------------------------------------

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

  • 一致性读不适用于DROP TABLE,因为表已经被InnoDB销毁了
  • 一致性读不适用于ALTER TABLE,因为ALTER TABLE实际是生成一张原始表的临时表,并在构建完成后删除原始表。在事务中进行一致的读取时,新表中的行不可见,这种情况下事务会返回 ER_TABLE_DEF_CHANGED错误(表定义已更改,请重试事务)

在没有指定FOR UPDATE或者LOCK IN SHARE MODE的情况下INSERT INTO ... SELECT,UPDATE ...(SELECT)和CREATE TABLE ...等语句中的的读取会有以下差异:

  • 默认情况下,就像READ COMMITTED一样,即使在同一事务中,每个一致性读都会建立和读取自己的快照
  • 如果将innodb_locks_unsafe_for_binlog设置为了enable并且事务隔离级别不是SERIALIZABLE,则读操作不会再行上加锁

推荐阅读更多精彩内容