1.首先给出一张图,初步了解事务隔离级别有哪些。
*** 幻读和不可重复读的区别?
幻读主要是针对范围查询,例如插入和删除
不可重复读是针对有一条记录,例如列信息被更新了
2.出现场景
这位老哥的博客写的很清楚了, 我就不在累赘的重复写了,
3.如何解决幻读
1)首先来了解InnoDB Locking(InnoDB锁)
首先锁类型分为如下几种
(1)共享锁和独占锁
(2)意向锁
(3)记录锁
(4)间隙锁
(5)next-key Lock
(6)插入意向锁
(7)自增锁
(8)空间索引断言锁
2)共享锁和独占锁
InnoDB有两个标准的行级锁,即共享锁(S)和独占锁(X)
共享锁允许持锁事务读取一行
独占锁允许持锁事务更新或者删除一行
表数据:
age
4
7
12
间隙有(-无穷,4],(4,7],(7,12],(12,+无穷]
3)意向锁
InnoDB 支持多粒度的锁,允许表级锁和行级锁共存。一个类似于 LOCK TABLES ... WRITE 的语句会获得这个表的 x 锁,LOCK TABLES ... READ的语句获得这个表的S锁。
为了实现多粒度锁,InnoDB使用了意向锁(简称I锁)。I锁是表明一个事务稍后要获得针对一行记录的某种锁(s or x)的对应表的表级锁,有两种:
意向排它锁(简称 IX 锁)表明一个事务意图在某个表中设置某些行的 X 锁
意向共享锁(简称 IS 锁)表明一个事务意图在某个表中设置某些行的 S 锁
例如:LOCK TABLES my_tablename WRITE 获得表级X锁
LOCK TABLES my_tablename READ 获得表级别S锁
SELECT .... LOCK IN SHARE MODE 获得意向IS锁
SELECT .... FOR UPDATE 获得意向IX锁
意向锁的原则如下:
一个事务必须先持有该表上的 IS 或者更强的锁才能持有该表中某行的 S 锁
一个事务必须先持有该表上的 IX 锁才能持有该表中某行的 X 锁
各个锁的兼容性如下:
4)记录锁
记录锁针对索引记录。举个例子:SELECT age FROM t WHERE age = 7 FOR UPDATE; 阻止其他所有事务插入、修改或者删除 t.age 是 7 的行。
记录锁总是锁定索引记录,即使表没有索引(这种情况下,InnoDB会创建隐式的索引,并使用这个索引实施记录锁)。
实际情况:当age列上有唯一索引时,锁住的就是age=7这条数据。 当age列上存在非唯一索引时,insert会锁住[4,12]这个区间,但允许更新操作(除age=7外) 不存在索引的时,无法insert/update/delete操作,因为会锁住整个表
5)间隙锁
间隙锁(gap)是索引记录之间上的锁,或者说第一个索引记录之前或最后一个索引记录之后的间隔上的锁。例如,SELECT age FROM t WHERE age BETWEEN 10 and 20 FOR UPDATE;会锁住age在10到20这个区间的插入操作,以及锁住这个范围内存在数据的update操作,假设这个范围内只有age为11,15,那么update更新age为12的数据则不会被阻塞,因为索引记录上没有该值。
6)next-key锁
next-key锁是记录锁和间隙锁的组合。
当age存在唯一索引时,执行select * from t where age > 8 for update时,insert会锁住(4,7],(7,12],(12,+无穷),
update会锁住[4,12]内存在的数据,delete会锁住大于8存在的数据。
当age不存在非唯一索引时,执行select * from t where age > 8 for update时,insert会锁住(7,12)、(12,+无穷),
update会锁住[4,+无穷]整个区间的数据,delete会锁住大于8的数据。
当age不存在索引时,执行select * from t where age > 8 for update时,无法insert/update/delete操作,因为会锁住整个表
InnoDB采用这种锁的模式,防止隔离级别为 REPEATABLE READ中的幻读现象。
7)插入意向锁
插入意向锁是在插入一行记录操作之前设置的一种间隙锁,这个锁释放了一种插入方式的信号,亦即多个事务在相同的索引间隙插入时如果不是插入间隙中相同的位置就不需要互相等待。假设有索引值4、7,几个不同的事务准备插入5、6,每个锁都在获得插入行的独占锁之前用插入意向锁各自锁住了4、7之间的间隙,但是不阻塞对方因为插入行不冲突
8)自增锁
自增锁是一个特殊的表级锁,事务插入自增列的时候需要获取,最简单情况下如果一个事务插入一个值到表中,任何其他事务都要等待,这样第一个事物才能获得连续的主键值
9)空间索引断言锁
InnoDB 支持针对含空间数据的列的列空间索引,要处理空间索引的锁,next-key处理的不好,不能支持可重复读和序列化的事务隔离级别。因为多维数据中没有绝对的顺序概念,所以不能明确什么是next key(下一个键)。
为了支持含空间索引的表的事务隔离级别,InnoDB 使用了断言锁,一个空间索引包含了最小外接矩形(MBR)值,所以InnoDB 通过为查询使用的MBR设置断言锁保证索引了一致读。其他事务不能插入符合当前事务查询条件的行