一、幻读是什么?
在MySQL的隔离级别RR下,幻读是指读到了新增的数据。
1.幻读问题一:只给某一行加锁
for update就会给该条sql加上行锁,但是一定就不会有问题了吗?
问题一
统一放一起就是这样的
update t set d=5 where id=0; /*(0,0,5)*/
update t set c=5 where id=0; /*(0,5,5)*/
insert into t values(1,1,5); /*(1,1,5)*/
update t set c=5 where id=1; /*(1,5,5)*/
update t set d=100 where d=5;/*所有d=5的行,d改成100*/
原因:“select * from t where d=5 for update 这条语句只给 d=5 这一行,也就是 id=5 的这一行加锁”导致的。
2.幻读问题二:新增的加不了锁
问题二
可以看到,按照日志顺序执行,id=0 这一行的最终结果也是 (0,5,5)。所以,id=0 这一行的问题解决了。但同时你也可以看到,id=1 这一行,在数据库里面的结果是 (1,5,5),而根据 binlog 的执行结果是 (1,5,100),也就是说幻读的问题还是没有解决。为什么我们已经这么“凶残”地,把所有的记录都上了锁,还是阻止不了 id=1 这一行的插入和更新呢?原因很简单。在 T3 时刻,我们给所有行加锁的时候,id=1 这一行还不存在,不存在也就加不上锁。
也就是说,即使把所有的记录都加上锁,还是阻止不了新插入的记录
二、怎么解决幻读?
用间隙锁(Gap lock)+行锁
间隙锁和行锁合称 next-key lock,是一个左开右闭区间。
例如:0,5,6三行分别是 (-∞,0]、(0,5]、(5,6]、(6, +supremum]。
间隙锁之间的兼容关系是怎样的?
在同时插入不存在的行时,关系如下
image.png
三、next-key lock一定是安全的吗?
不一定,因为如果同时对一个间隙进行插入操作的时候可能会出现死锁,间隙锁只存在可重复读的隔离级别下。
四、读提交+把binlog的格式改成row
binlog_format=row
1.这样会有问题吗?
2.在备份期间,备份线程用的是可重复读,而业务线程用的是读提交。同时存在两种事务隔离级别,会不会有问题?
3.这两个不同的隔离级别现象有什么不一样的,关于我们的业务,“用读提交就够了”这个结论是怎么得到的?
五、思考题
思考题
如果你之前没有了解过本篇文章的相关内容,一定觉得这三个语句简直是风马牛不相及。但实际上,这里 session B 和 session C 的 insert 语句都会进入锁等待状态。你可以试着分析一下,出现这种情况的原因是什么?