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(也就是d=5由于d上没有索引,所以会走主键索引树)加行锁,我们可以分析以下SessionA会出现什么情况:
- Q1查询只会返回id=5这一行,也就是(5,5,5)
- T2时刻Session B把id为0这一行的d值改成5,因此Q2查询查出来的是id=0和id=5的这两行(0,0,5),(5,5,5)
- T4时刻,Session C插入一行(1,1,5),因此Q3查询查出来的是id=0、1、5的这三行(0,0,5),(1,1,5),(5,5,5)
【MySQL如何解决幻读】Q3读到id=1这一行的现象成为幻读 。幻读是指一个事务在前后两次查询同一个范围的数据的时候,后一次查询查到了第一次查询没有查到的行 。
在可重复隔离级别下,普通的查询时快照读,是无法看到别的事务插入的数据的,只有当前读才会出现幻读 。
幻读有什么问题?
- 语义上带来了破坏 。
- 数据一致性上会有问题 。
文章插图
- 经过T1时刻,id=5这一行变成(5,5,100),当然这个结果最终是在T6时刻进行提交
- 经过T2时刻,id=0这一行变成了(0,5,5)
- 经过T4时刻,表里面新生成了一行(1,5,5)
- T2时刻,Session B事务提交,写入两条语句;
- T4时刻,Session C事务提交,写入两条语句;
- T6时刻,Session A事务提交,写入update t set d=100 where d=5这条语句
-- binlog的模式是statement;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*/
这个binlog如果被拿到备库执行或者用来克隆一个数据库,这三行的结果会变成(0,5,100)、(1,5,100)和(5,5,100),此时id=0和id=1这两行就出现了数据不一致 。如何解决幻读?
产生幻读的原因就是行锁只能锁住行,插入动作更新的是记录之间的间隙 。因此为了解决幻读问题,InnoDB引入了间隙锁 。
什么是间隙锁?
在文章开始的时候我们插入了6条数据,这就会产生7个间隙,如下:
- (-∞, 0)
- (0, 5)
- (5, 10)
- (10, 15)
- (15, 20)
- (20, 25)
- (25, +∞)
间隙锁之间没有冲突,跟间隙锁冲突的是往这个间隙中插入一个记录的操作 。
什么是next-key lock?
间隙锁和和行锁合称next-key lock,每个next-key lock都是前开后闭区间 。
select * from t where d=5 for update在执行的时候将形成7个next-key lock:
- (-∞, 0]
- (0, 5]
- (5, 10]
- (10, 15]
- (15, 20]
- (20, 25]
- (25, +∞]
间隙锁导致死锁?
文章插图
Session B的insert会被阻塞,Session A在执行insert的时候会检测到死锁,如下图:
文章插图
- Session A执行select for update,由于id=8这一行并不存在,因此会加上间隙锁(5,10)
推荐阅读
- 专家解读:《网络安全审查办法》实施 企业安全工作如何有的放矢?
- 如何购买域名和服务器?服务器和域名大概需要多少钱?
- 如何用FFMpeg生成视频
- 这才是解决U盘问题的正确方式!学会这方法,轻松找出故障原因
- 翡翠|判定翡翠的价值,无论质地如何,是不是越大价值越高?
- 如何在夏侯惇玩,王者荣耀
- 如何给孕妇炖乌鸡汤
- 如何分辨普洱和红茶,稻香普洱茶的功效和作用
- 如何给成吉思汗穿上王者荣耀
- win10下解决EVE模拟器连接SecureCRT单窗口多标签