mysql 幻读
什么是幻读?
InnoDB默认级别为可重复读,可重复读会产生问题就是幻读。事务A按照一定条件进行数据读取,期间事务B插入了相同搜索条件的新数据,事务A再次按照原先条件进行读取时,发现了事务B新插入的数据称之为幻读。如果事务中都是用快照读,那么不会产生幻读的问题,但是快照读和当前读一起使用的时候就会产生幻读。
不可重复读侧重于update这种操作,同一条数据前后读起来不一样的情况,幻读侧重于insert delete这种操作,前后两次select 数据的数量会发生变化
举个例子:
事务A 第一步 select * 第二步 update 所有字段 第三步 再次select *
事务B 执行了insert 一条语句
幻读第一种情况: 当事务A 刚执行完第一步,事务B insert一条,导致事务A update执行完,再次select发现多了一条数据
幻读第二种情况: 当事务A 刚执行完第二步,事务B insert一条,导致事务A 再次select 发现有一条数据没有update字段
InnoDB如何解决幻读的?
Mvcc+行锁+间隙锁
什么是间隙锁?
正常等值条件 并且值存在的情况下加的是行锁
如果等值条件 值不存在的情况下加的是间隙锁,或者范围查询,加的也是间隙锁
举个例子:
根据主键id,不只是有五个行锁,还会有六个间隙锁,左开右闭原则,(-∞,5](5,10](10,15](15,20](20,25](25,+supernum]
例如 select * from table where id = 10 for update; 等值条件,id是存在的,加行锁就可以了
select * from table where id = 7 for update; 等值条件,id不存在,加(5,10] 间隙锁,这范围间不允许插入数据,直到这个事务提交完成释放锁
select * from table where id > 24; 范围条件,加间隙锁
通过行锁+间隙锁的机制保证了事务A select之后,其他事务相应的insert操作会阻塞
间隙锁跟MVCC一起工作。实现事务处理:
Repeatable Read隔离级别: 采用Next-key Lock(间隙锁) 来解决幻读问题.因此 Mysql 在Repeatable下面 幻读,可重复读,脏读 三者都不会发生
Read Committed隔离级别:采用Record锁,不会出现脏读,但是会产生"幻读"问题. 也会出现可重复读
间隙锁简介:
MySQL InnoDB支持三种行锁定方式:InnoDB的默认加锁方式是next-key 锁
行锁(Record Lock):锁直接加在索引记录上面,锁住的是key。
间隙锁(Gap Lock):锁定索引记录间隙,确保索引记录的间隙不变。间隙锁是针对事务隔离级别为可重复读或以上级别而已的。
Next-Key Lock :行锁和间隙锁组合起来就叫Next-Key Lock。
默认情况下,InnoDB工作在可重复读(Repeatable Read)隔离级别下,并且会以Next-Key Lock的方式对数据行进行加锁,这样可以有效防止幻读的发生。Next-Key Lock是行锁和间隙锁的组合,当InnoDB扫描索引记录的时候,会首先对索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。加上间隙锁之后,其他事务就不能在这个间隙修改或者插入记录。 read committed隔离级别下Gap Lock在InnoDB的唯一作用就是防止其他事务的插入操作,以此防止幻读的发生。Innodb自动使用间隙锁的条件:
(1)必须在Repeatable Read级别下
(2)检索条件必须有索引(没有索引的话,mysql会全表扫描,那样会锁定整张表所有的记录,包括不存在的记录,此时其他事务不能修改不能删除不能添加)