美文网首页
mysql可重复读的幻读解决方案

mysql可重复读的幻读解决方案

作者: 梦想实现家_Z | 来源:发表于2021-08-01 22:27 被阅读0次

首先需要明确的就是“幻读”概念:隔离级别是可重复读,在一个事务中前后两次查询,查到了其他事务insert进来的数据。
强调的是读取到了其他事务插入进来的数据。
下面来论证一下可重复读下幻读的解决方案

# 建表语句
CREATE TABLE `test`  (
  `id` int(11) NOT NULL COMMENT '主键',
  `d` int(11) NOT NULL,
  `c` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '测试表';
# 插入数据
INSERT INTO `test`(`id`, `d`, `c`) VALUES (0, 0, 0);
INSERT INTO `test`(`id`, `d`, `c`) VALUES (2, 2, 2);
INSERT INTO `test`(`id`, `d`, `c`) VALUES (3, 3, 3);
INSERT INTO `test`(`id`, `d`, `c`) VALUES (4, 4, 4);
INSERT INTO `test`(`id`, `d`, `c`) VALUES (5, 5, 5);
INSERT INTO `test`(`id`, `d`, `c`) VALUES (6, 6, 6);
INSERT INTO `test`(`id`, `d`, `c`) VALUES (7, 7, 7);
INSERT INTO `test`(`id`, `d`, `c`) VALUES (8, 8, 8);
INSERT INTO `test`(`id`, `d`, `c`) VALUES (9, 9, 9);
T1 T2 T3
begin; select * from TABLE where d = 5 for update;
update TABLE set d = 5 where id = 0;
select * from TABLE where d = 5 for update;
insert into TABLE values(1,5,1);
select * from TABLE where d = 5 for update; commit;

先明确一下,for update语法就是当前读,也就是查询当前已经提交的数据,并且是带悲观锁的。没有for update就是快照读,也就是根据readView读取的undolog中的数据。

  • 当T1开启事务,执行第一条select语句时,查出来的结果如下:
id d c
5 5 5
  • 假如该查询只锁定一行,也就是id=5的这一行数据,那么在T2阶段id=0并不会被锁定,那么update执行成功后,id=0那行数据被设置为d=5。此时T1阶段第二个select查询结果如下:
id d c
5 5 5
0 5 0
  • T3阶段执行插入语句后,T1阶段第三个查询得出的结果如下:
id d c
5 5 5
0 5 0
1 5 1

如果按照以上猜想,那么整个执行结果就违背了可重复读的隔离级别了。


那么我们再假设select * from TABLE where d = 5 for update;这条语句锁定的是所有被扫描到的数据。

  • 按照上述执行逻辑,我们在T1阶段第一个select读取到的数据:
id d c
5 5 5
  • T1阶段第二个select读取到的数据:
id d c
5 5 5

这是因为T2阶段的update会被阻塞住,毕竟所有被扫描到的记录都被锁定了。

  • 但是在T3阶段依然会执行,T3阶段做的是insert操作,本身这条记录在表中都不存在的,也就不会被阻塞。那么T1阶段第三个select查询结果如下:
id d c
5 5 5
1 5 1

按照上述推理过程,很显然,即使锁定所有扫描到的数据行,也依然存在幻读的情况。违背了可重复读的隔离级别。


针对这个情况,我们要解决幻读的问题,那么就要求针对所有被扫描的记录行以及还不存在的d=5的记录行都给锁住。

  • 在T1阶段当执行第一条select语句时,所有被扫描的记录行都锁住,包括d=5的不存在的记录行。那么T2执行的时候,就会被阻塞住,等待T1结束。
id d c
5 5 5
  • 当执行到T1阶段的第二个select时,因为T2还在等待T1结束,所以查询结果一样
id d c
5 5 5
  • 当执行T3阶段的insert语句时,因为所有d=5的不存在的记录行也被锁住了,也就是间隙被锁住了,那么T3的insert语句也被阻塞,等待T1结束。那么T1阶段最后一条select语句执行结果如下:
id d c
5 5 5

至此,当前查询结果完全满足可重复读的隔离级别。


通过以上推论,我们可以总结一下,在可重复读的隔离级别下,解决幻读除了需要锁定所有扫描到的记录行外,还需要锁定行之间的间隙,也就是通过间隙锁来解决幻读的问题。

相关文章

  • MySQL的可重复读级别能解决幻读吗

    转载 MySQL的可重复读级别能解决幻读吗MySQL事务隔离级别的实现原理

  • MYSQL -锁机制01

    一、前提1、MySQL版本:5.7.192、引擎:InnoDB3、隔离机制:可重复读。 二、不可重复读与幻读的区别...

  • mysql可重复读的幻读解决方案

    首先需要明确的就是“幻读”概念:隔离级别是可重复读,在一个事务中前后两次查询,查到了其他事务insert进来的数据...

  • 数据库锁机制

    本文所有内容基于MySQL/InnoDB引擎。 并发事务解决方案 脏读、不可重复读和幻读都是数据库读一致性问题,需...

  • spring事务整理

    隔离级别 1、默认使用数据库的(mysql:可重复读,oracle:已提交读) 2、未提交读 会出现脏读、幻读、不...

  • mysql知识点

    mysql概览 一些基本问题 事务 事务相关基本问题 脏读 丢失修改 不可重复读 幻读 不可重复读 vs 幻读不可...

  • MySQL可重复读防止幻读

    接上篇事务隔离级别和幻读,留了个坑,没想到竟然过了10天,时间不注意真的过的好快。顺便提下,简书图片链接是属于网站...

  • 66 MVCC 多版本控制模型原理与间隙锁,悲观锁。乐观锁

    1 MYSQL 事务隔离级别 四种隔离级别: 脏读,可重复读,幻读总结2,详细画图分析 MVCC 多版本并发控制与...

  • mysql 解决可提交读、可重复读、幻读

    1.重要的概念 mysql的锁有哪些 这张图本人觉得总结得挺好的,在一般的互联网项目中,基本上用的都是Innodb...

  • 1.4 MVCC多版本控制的实现

    前面提到的在mysql中事务的级别默认是repeatable read(可重复读),其有出现幻读的可能性,即事务A...

网友评论

      本文标题:mysql可重复读的幻读解决方案

      本文链接:https://www.haomeiwen.com/subject/otorvltx.html