乐趣区

关于mysql: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 锁。

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

退出移动版