一致性非锁定读

一,定义
一致性非锁定读指InnoDB引擎通过行多版本控制的形式(也就是多版本并发管制)来读取以后执行工夫数据库中行的数据。如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会因而去期待行上锁(对应X锁)的开释,而是会去读取行的一个快照数据。

快照数据:是指该行的之前版本数据,它是通过undo段实现的,次要用在事务中回滚数据。另外读取快照数据不须要上锁。
多版本并发管制:快照数据其实就是以后行数据之前的历史版本,一个行记录可能有不止一个快照数据,由此而来的并发管制称为多版本并发管制。

二,不同隔离级别对一致性非锁定读的不同体现
在事务隔离级别READ COMMITTED(下简称RC)和REPEATABLE READ(下简称RR)下,InnoDB应用非一致性锁定读。

  • 在RC级别下,对于快照数据,非一致性读总是读取被锁定行的最新一份快照数据。
  • 在RR级别下,对于快照数据,非一致性读总是读取本事务开始时的行数据版本。

三,示例

首先在以后数据库连贯会话A中执行如下SQL:
session A

mysql>BEGIN;query OK,0 rows affected(0.00 sec)mysql>select * from parent where id = 1;+----+| id |+----+| 1 |+----+1 row in set (0.00 sec)

会话A中已通过显式开启了一个事务,并读取id为1的数据,但事务没没有完结。与此同时,用户再开启另一个会话B,操作如下:

mysql>BEGIN;query OK,0 rows affected (0.00 sec);mysql>update parent set id=3 where id=1;query ok, 1 row affectedRows matched :1 Changed : 1 Warning:0

同样会话B的事务并没有提交,因为会话B执行的是一个批改执行所以id=1的行加了X锁。如果其它会话再次读取id为1的记录,在RC与RR隔离级别下会应用非锁定的一致性读。这个时候会话A在上次未提交的事务里再次读取id为1的操作,不论是RC还是RR都能够查到id=1的记录:

mysql>select * from parent where id=1;+----+| id |+----+| 1 |+----+1 row in set (0.00 sec)

分RC与RR两种级别进行剖析:

  • RC总是读取被锁定行的最新一份数据,也就是最新的一个版本,因为以后id=1的数据只被批改了1次,只有一个版本,所以RC下读到了id=1的版本。
  • RR总是读取事务开始时的行数据版本,同样因为id=1只有一个版本,这个版本也是事务开始时的那个版本,所以RR下也读到了id=1的版本

这里要留神的是,只管RC与RR都读到了id=1的数据,但它们读到的起因是不同的。
为了进一步看到差别,咱们将会话B的事务进行提交,而后用RC隔离级别下在会话A中再次运行如下查问:

mysql>select @@tx_isolation;@@tx_isolation:READ-COMMITTED;1 row in set (0.00 sec)mysql>select * from parent where id=1;empty set (0.00 sec)

再将隔离级别换成RR:

mysql>select @@tx_isolation;@@tx_isolation:REPEATABLE-READ;1 row in set (0.00 sec)mysql>select * from parent where id=1;+----+| id |+----+| 1 |+----+1 row in set (0.00 sec)

因为会话B将id改为3的事务已提交,所以会存在两个版本数据:id为3的最新的版本数据与事务开始时id为1的数据。

  • 对于RC在行锁定的状况下,它会读取该行的最新一个快照,但最新的快照数据里id为3,所以没读到(呈现了不可反复读的状况);
  • 对于RR在行锁定的状况下,它读取本事务开始时的快照,所以读取了id为1的数据。

画个表格看下会话B将事务提交后的状况:

快照读实现后果
RC应用最新快照版本没读到数据,呈现了不可反复读
RR本事务开始时的版本id=1的数据

一致性锁定读

用户显式地对数据库读取操作进行了加锁,InnoDB对于SELECT语句反对两种一致性的锁定读操作:

  1. select...for update
  2. select...lock in share mode

对于第一种会对读的行记录加一个X锁,其它事务不能对已锁定的行加工作锁;
对于第二种会对读的行记录加一个S锁,其它事务能够加S锁但不能加X锁。

本文来自于《mysql技术底细 InnoDB存储引擎》一书