一致性非锁定读(consistent nonlocking read)
一致性非锁定读是InnoDB存储引擎通过多版本控制(multi versioning)的方式来读取当前执行时间数据库中的数据。如果被读的数据行被加了排他锁,在读取这行数据的时候并不会等待锁释放,而是读取该行的一个快照数据。 之所以称为非锁定读,因为不需要等待被访问行的X锁的释放。快照数据是指修改行之前的数据版本,该实现通过undo段来完成。
非锁定读的方式极大提高了数据库的并发性。在InnoDB存储引擎中,这是默认的读取方式。
快照数据其实就是当前行数据的一个历史版本,每行记录可能有多个版本。这种技术成为行多版本技术。由此带来的并发控制,成为多版本并发控制(Multi Version Concurrency Control,MVCC)。
在事务的隔离级别,READ COMMITED和REPEATABLE READ(InnoDB存储引擎默认的事物隔离级别)下,对快照数据的定义不同。在READ COMMITTED事务隔离级别下,对于快照数据,非一致性读总是读取被锁定行的最新一份快照数据。而在REPEATABLE READ事务隔离级别下,对于快照数据,非一致性读总是读取事务开始时的数据版本。
也就是说在READ COMMITTED事务隔离级别下,非锁定读读取到的数据是最新的快照版本数据,也就是可以读到另一个事务已经提交了的快照数据。而在REPEATABLE READ下,只会读到事务开始前的数据。
简单举例说明:
需要特别注意的是,对于READ COMMITTED的事物隔离级别而言,从数据库理论的角度来看,其违反了事物ACID中的I的特性,即隔离性。
另外可以通过:SELECT @@tx_isolation 可以查看当前数据库的隔离级别
一致性锁定读
在默认情况下,InnoDB存储引擎对数据采用的是一致性非锁定读。但是有些情况下为了保证数据逻辑的一致性,需要对SELECT的操作加锁。InnoDB存储引擎对于SELECT语句支持两种一致性的锁定读(locking read)操作。
1、 SELECT …… FOR UPDATE
2、 SELECT …… LOCK IN SHARE MODE
其中,SELECT …… FOR UPDATE对读取的记录加一个X锁(排它锁),其他事务不能对已锁定的行加任何锁。而SELECT …… LOCK IN SHARE MODE是对读取的记录加一个S锁(共享锁),其他事物可以向被锁定的行加S锁,但是如果加X锁,则会被阻塞。
对于一致性非锁定读,即使读取的行已经被执行了 SELECT …… FOR UPDATE,也是可以进行读取的,这和上面讨论的情况一样。此外,SELECT …… FOR UPDATE, SELECT …… LOCK IN SHARE MODE必须在一个事物中,当事物提交了,锁也就释放了。因此在使用上诉两句SELECT锁定语句时,务必加上BEGIN,START TRANSACTION 或者SET AUTOCOMMIT=0
SELECT …… FOR UPDATE 简单举例说明:
session A | session B |
begin; | |
select * from t1 for update; | |
update t1 set name='王五' where id=2; 一直等待,无返回结果 |
|
commit; | |
session A commit后,释放排它锁, 上面的update返回结果 |
实验截图
修改会等待锁释放
查询会按一致性非锁定读来读
SELECT …… LOCK IN SHARE MODE 举例说明:
一样会阻塞修改,可以正常查询
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from t1 lock in share mode; +----+--------+ | id | name | +----+--------+ | 1 | 张三 | | 2 | 王五 | +----+--------+ 2 rows in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec)
mysql> update t1 set name='王五' where id=2; Query OK, 0 rows affected (41.10 sec) Rows matched: 1 Changed: 0 Warnings: 0