InnoDB一致性非锁定读与一致性锁定读

一致性非锁定读
一,定义
一致性非锁定读指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 affected Rows 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锁。
【InnoDB一致性非锁定读与一致性锁定读】本文来自于《mysql技术内幕 InnoDB存储引擎》一书

    推荐阅读