innodb的默认事务隔离级别是rr(可重复读)。它的实现技术是mvcc(MVCC只在读提交可重复读两种隔离级别下工作)。基于版本的控制协议。该技术不仅可以保证innodb的可重复读,而且可以防止幻读。但是它防止的是快照读,也就是读取的数据虽然是一致的,但是数据是历史数据。
为什么说他读的是历史数据的可重复读呢,因为根据这个mvcc的查询规则,新数据他是看不到的,无感知的。
根据mvcc查询和更新的原则:SELECT操作的查询结果要同时满足条件:
1、只会查询版本号小于等于当前版本号的数据作为结果返回,保证了这个数据要么是当前事务修改过的,要么是事务开始之前就已经存在的。
2、行的删除版本要么未定义,要么大于当前事务版本号,这可以确保事务读取到的行,在事务开始之前未被删除
间隙锁是在可重复读的隔离级别下为了解决幻读引入的锁机制。
加锁规则:
- 原则 1:加锁的基本单位是 next-key lock。希望你还记得,next-key lock 是前开后闭区间。
- 原则 2:查找过程中访问到的对象才会加锁。limit遍历访问不到的话不会加锁
- 优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。
- 优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。
- 唯一索引上的范围查询会访问到不满足条件的第一个值为止。
- 给普通索引加锁的时候,还会给存在的记录加行锁
加锁规则可以这么理解:
主键/唯一索引查询只有锁住多条记录或者一条不存在的记录的时候,才会产生间隙锁,指定给某条存在的记录加锁的时候,只会加记录锁,不会产生间隙锁;
在id=0,id=5存在的情况下,查询id=3或者查询唯一索引u=3,会产生间隙锁(0,5)
在id=0,id=5,id=10存在的情况下,查询id>3 and id <8,会产生间隙锁(0,5】,(5,10】
在id=0,id=5,id=10存在的情况下,查询唯一索引u>3 and u <8,会产生间隙锁(0,5】,(5,10)
普通索引等值查询,值不存在的情况下,查询c=3,间隙锁为(0,5)
普通索引等值查询,值存在的情况下,查询c=5,间隙锁为(0,5】(5,10)
普通索引范围查询,在id=0,id=5,id=10存在的情况下,查询id>3 and id <6,会产生间隙锁(0,5】,(5,10)
数据库表和数据如下:
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
`d` int(11) DEFAULT NULL,
`u` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `u_index` (`u`),
KEY `c` (`c`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci