MySQL 幻术月读-幻读

本文探讨了MySQL中InnoDB的可重复读隔离级别下出现的幻读问题。幻读是指在当前读模式下,事务前后两次查询同一范围的数据,第二次查询出现了第一次未见的新行。幻读产生的原因是行锁无法锁定尚未存在的记录。为了解决幻读,InnoDB引入了间隙锁,通过锁定记录之间的间隙防止新行插入,从而确保事务的语义完整性和数据一致性。然而,间隙锁也会带来并发度降低和死锁风险。设置隔离级别为读提交可以避免间隙锁,但可能需要配合binlog_format=row以保证数据一致性。
摘要由CSDN通过智能技术生成

InnoDB的默认事务隔离级别是可重复读。

课前准备

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);

幻读是什么?

现在,我们就来分析一下,如果只在id=5这一行加锁,而其他行的不加锁的话,会怎么样。

下面先来看一下这个场景:

图 1 假设只在id=5这一行加行锁

可以看到,session A里执行了三次查询,分别是Q1、Q2和Q3。它们的SQL语句相同,都是select * from t where d=5 for update。这个语句的意思你应该很清楚了,查所有d=5的行,而且使用的是当前读,并且加上写锁。现在,我们来看一下这三条SQL语句,分别会返回什么结果。

Q1只返回id=5这一行;

在T2时刻,session B把id=0这一行的d值改成了5,因此T3时刻Q2查出来的是id=0和id=5这两行;

在T4时刻,session C又插入一行(1,1,5),因此T5时刻Q3查出来的是id=0、id=1和id=5的这三行。

其中,Q3读到id=1这一行的现象,被称为“幻读”。也就是说,幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。

这里,我需要对“幻读”做一个说明:

在可重复读隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的。因此,幻读在“当前读”下才会出现。

上面session B的修改结果,被session A之后的select语句用“当前读”看到,不能称为幻读。幻读仅专指“新插入的行”。

这三个查询都是加了for update,都是当前读。而当前读的规则,就是要能读到所有已经提交的记录的最新值。并且,session B和sessionC的两条语句,执行后就会提交,所以Q2和Q3就是应该看到这两个事务的操作效果,而且也看到了,这跟事务的可见性规则并不矛盾。

但是,这是不是真的没问题呢?

不,这里还真就有问题。

幻读有什么问题?

首先是语义上的。session A在T1时刻就声明了,“我要把所有d=5的行锁住,不准别的事务进行读写操作”。而实际上,这个语义被破坏了。

如果现在这样看感觉还不明显的话,我再往session B和session C里面分别加一条SQL语句,你再看看会出现什么现象。

图 2 假设只在id=5这一行加行锁--语义被破坏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值