InnoDB的锁定读与一致性非锁定读
本文主要介绍InnoDB的:锁定读和一致性非锁定读
锁定读(Locking Reads)
在一个事务中查询数据时,普通的SELECT语句不会对查询的数据进行加锁,其他事务仍可以对查询的数据执行更新和删除操作。因此,InnoDB提供了两种类型的锁定读来保证额外的安全性:
- SELECT ... LOCK IN SHARE MODE
- SELECT ... FOR UPDATE
SELECT ... LOCK IN SHARE MODE: 对读取的行添加S锁,其他事物可以对这些行添加S锁,若添加X锁,则会被阻塞。
SELECT ... FOR UPDATE: 会对查询的行及相关联的索引记录加X锁,其他事务请求的S锁或X锁都会被阻塞。
当事务提交或回滚后,通过这两个语句添加的锁都会被释放。
注意:只有在自动提交被禁用时,SELECT FOR UPDATE才可以锁定行,若开启自动提交,则匹配的行不会被锁定。
一致性非锁定读(Consistent Nonlocking Reads)
MySQL官方文档对弈一致性读的定义:
A consistent read means that InnoDB uses multi-versioning to present to a query a snapshot of the database at a point in time.
就是说:一致性读意味着InnoDB使用多版本控制来实现一个查询,这个查询所依赖的数据是基于某个时间点的数据库快照
。
注意:是数据库的快照,而不是要查询的那个表的快照。
在事务隔离级别设置为READ COMMITTED和REPEATABLE READ时,默认情况下InnoDB是通过一致性读来处理SELECT语句的。一致性读不会对它访问的表设置任何锁,因此,在一个表上进行一致性读的同时,其他session可以随意修改这些表。
一致性读是使用基于时间点的快照信息来呈现查询结果的读取操作,而不管其他事务在同一时间产生的变更。如果查询的数据已被其他事务改变,InnoDB会基于undo log的内容来重建最初的数据。
两种事务隔离级别下,读取的快照数据的区别:
- 在REPEATABLE READ事务隔离级别下,同一事务内的所有一致性读都会读取到
该事务中第一个一致性读建立的快照
。通过提交当前事务并发起一个新的查询,可以看得到新的快照。 - 在READ COMMITTED事务隔离级别下,同一事务内的每个一致性读总是读取
本次一致性读建立的快照
。(即,快照的时间点是基于本次一致性读的时间点,也就是最新的数据)
假设当前事务隔离级别为REPEATABLE READ,当发出一个一致性读(也就是一个普通的SELECT语句)时,InnoDB会根据这个查询给此事务分配一个时间点。若其他事务在此时间点之后删除了一行并提交,那么此事务不会看到其他事务产生的影响(即,还是会看到被删除的行)。插入和更新操作也一样。看如下实验
实验
create table test_cr(
a int,
b int
)ENGINE=InnoDB DEFAULT CHARSET=utf8
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
实验结果如下图:
从图中可以看出,在REPEATABLE READ事务隔离级别下,session A的事务以第一次执行select语句的时间点生成了快照,之后的select查询都是使用的这次快照数据。针对session B中事务作出的更新操作,若变更在session A中第一次select操作之前被commit,则session A的第一次select语句可以看到session B中的变更,否则看不到session B中的变更。
参考: