20 幻读
20 幻读
幻读
幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。
- 幻读在“当前读”下才会出现;
- 幻读仅专指“新插入的行”。
幻读会出现的问题:
- 语义的问题;
- 数据一致性问题;
解决方案:
产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙”。因此,为了解决幻读问题,InnoDB只好引入新的锁,也就是间隙锁(Gap Lock)。
间隙锁和行锁合称next-key lock,每个next-key lock是前开后闭区间。
间隙锁和 next-key lock 的引入,帮我们解决了幻读的问题,但同时也带来了一些“困扰”。
在删除数据的时候尽量加 limit。这样不仅可以控制删除数据的条数,让操作更安全,还可以减小加锁的范围。
可重复读隔离级别遵守两阶段锁协议,所有加锁的资源,都是在事务提交或者回滚的时候才释放的
读提交隔离级别下还有一个优化,即:语句执行过程中加上的行锁,在语句执行完成后,就要把“不满足条件的行”上的行锁直接释放了,不需要等到事务提交。
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
`d` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `c` (`c`)
) ENGINE=InnoDB;
insert into t values
(0,0,0),
(5,5,5),
(10,10,10),
(15,15,15),
(20,20,20),
(25,25,25);
下面这个图的执行序列中,为什么session B的insert语句会被堵住。
我们用上一篇的加锁规则来分析一下,看看session A的select语句加了哪些锁:
- 由于是order by c desc,第一个要定位的是索引c上“最右边的”c=20的行,所以会加上间隙锁(20,25)和next-key lock (15,20]。
- 在索引c上向左遍历,要扫描到c=10才停下来,所以next-key lock会加到(5,10],这正是阻塞session B的insert语句的原因。
- 在扫描过程中,c=20、c=15、c=10这三行都存在值,由于是select *,所以会在主键id上加三个行锁。
因此,session A 的select语句锁的范围就是:
- 索引c上 (5, 25);
- 主键索引上id=10、15、20三个行锁。
在MySQL中,会引发性能问题的慢查询,大体有以下三种可能:
- 索引没有设计好;
- SQL语句没写好;
- MySQL选错了索引。