mysql 幻读

在说幻读之前,首先要提一下什么是当前读和快照读
当前读
像select lock in share mode(共享锁), select for update ; update, insert ,delete(排他锁)这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
快照读
像不加锁的select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即MVCC,可以认为MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。

在事务中,读取数据一般都是快照读,但是事务中如果使用锁的时候比如,select * from test for update,读取的是当前读,但是不会改变mvcc中的 read view
举例


image.png

事务1


image.png

插入一条数据
image.png

事务1,由于mvcc没有读取到李四的数据
image.png

事务1加锁,可以看到用for update加锁之后,使用了当前读,获取都了最新的数据,去掉for update之后依然是mvcc的数据,这就说明了,加锁之后读取的数据不会同步到mvcc中


image.png

到这主要说明了事务中,主要还是说可重复读和读已提交隔离级别,在事务中获取数据,如果加锁之后,就是就变成了当前读.
在实际生产中,也主要关心的是可重复读的幻读
下面分析一下幻读的产生
数据库中有两条数据,其中李四,是在事务1启动之后添加,这里要注意,先开启事务1,再添加李四,事务1,select * from test,是可以读取李四这条数据,因为快照读,不是事务开启的时候,而是事务开启之后的第一条select才会开始快照读,这个一定要注意.
image.png

2.事务1,因为事务1,select之后添加李四,导致快照读没有李四
image.png

3.添加数据
image.png

4.事务1 select 依然只有张三
image.png

5.事务1 update,发现有一条数据被更新了,而且也查到了这条数据,但是没有查询到王五这条数据,update执行的时候自动的给数据加锁,根据上面分析,在事务中加锁是当前读,这个当前读只针对加锁的这一行数据,获取到了最新的数据,并把数据更新到了mvcc中


image.png

如果业务中不希望出现幻读的话,可以使用锁,
  1. 读取的时候使用for update,其余事务要修改数据的时候会阻塞
  2. 使用间隙锁 关于间隙锁,后续文章也分析到 因为间隙锁涉及到主键索引和非逐渐索引的区别

你可能感兴趣的:(mysql 幻读)