文章目录

  • 幻读是什么?
  • 幻读有什么问题?
    • 语义上的问题
    • 数据一致性的问题
  • 如何解决幻读?

幻读是什么?

  • 先来看以下这个场景
  • 可以看到,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 语句用“当前读”看到,不能称为幻读。幻读仅专指“新插入的行”。

幻读有什么问题?

语义上的问题

  • 首先以下需要的场景是,“我要把所有 d=5 的行锁住,不准别的事务对 d=5 的数据进行读写操作”
  • session B 的第二条语句 update t set c=5 where id=0,语义是“我把 id=0、d=5 这一行的 c 值,改成了 5”。
  • 由于在 T1 时刻,session A 还只是给 id=5 这一行加了行锁, 并没有给 id=0 这行加上锁。因此,session B 在 T2 时刻,是可以执行这两条 update 语句的。这样,就破坏了 session A 里 Q1 语句要锁住所有 d=5 的行的加锁声明。
  • session C 也是一样的道理,对 id=1 这一行的修改,也是破坏了 Q1 的加锁声明(原本声明 d=5 的都需要锁住,实际上这些后来新增或修改的 d=5 的记录都锁不住)。

数据一致性的问题

  • 我们知道,锁的设计是为了保证数据的一致性。而这个一致性,不止是数据库内部数据状态在此刻的一致性,还包含了数据和日志在逻辑上的一致性。
  • 为了说明这个问题,给 session A 在 T1 时刻再加一个更新语句,即:update t set d=100 where d=5。
  • update 的加锁语义和 select …for update 是一致的,所以这时候加上这条 update 语句也很合理。session A 声明说“要给 d=5 的语句加上锁”,就是为了要更新数据,新加的这条 update 语句就是把它认为加上了锁的这一行的 d 值修改成了 100
  • 数据库里的结果
    • 经过 T1 时刻,id=5 这一行变成 (5,5,100),当然这个结果最终是在 T6 时刻正式提交的 ;
    • 经过 T2 时刻,id=0 这一行变成 (0,5,5);
    • 经过 T4 时刻,表里面多了一行 (1,5,5);
    • 其他行跟这个执行序列无关,保持不变。
  • 这样看,这些数据也没啥问题,来看看 binlog 里面的内容
    • T2 时刻,session B 事务提交,写入了两条语句;
    • T4 时刻,session C 事务提交,写入了两条语句;
    • T6 时刻,session A 事务提交,写入了 update t set d=100 where d=5 这条语句。
  • 这里就出问题了,这个语句序列,不论是拿到备库去执行,还是以后用 binlog 来克隆一个库,这三行的结果,都变成了 (0,5,100)、(1,5,100) 和 (5,5,100)

如何解决幻读?

  • 现在知道了,产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙”。因此,为了解决幻读问题,InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock)
  • 间隙锁和之前碰到过的锁不太一样,比如行锁分为读锁和写锁,下图是两种类型锁的冲突关系:
  • 但是间隙锁不一样,跟间隙锁存在冲突关系的,是“往这个间隙中插入一个记录”这个操作,间隙锁之间都不存在冲突关系。
  • 间隙锁和行锁合称 next-key lock,每个 next-key lock 是前开后闭区间,加锁规则里面,包含了两个“原则”、两个“优化”和一个“bug”。
    • 原则 1:加锁的基本单位是 next-key lock。希望你还记得,next-key lock 是前开后闭区间。
    • 原则 2:查找过程中访问到的对象才会加锁。
    • 优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。
    • 优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。
    • 1 个 bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。
  • 关于间隙划分有一种理解是这样的,首先索引是有顺序的,相同索引值(比如 d=6)的索引一定是连在一起的,所以间隙锁锁的是第一个 d=6 开始到最后一个 d=6 的索引位置的 next 指针,锁住了的话就无法修改或者插入其他 d=6 的数据,也能解释唯一索引时会退化为行锁,因为唯一索引的 next 指向肯定不是相同值的索引元素,所以无需锁住 next 指针,只要锁住当前行即可。

笔记来源于《极客时间:MySQL实战45讲:幻读是什么,幻读有什么问题?》

幻读是什么,幻读有什么问题?相关推荐

  1. mysql 幻读理解_Mysql 幻读 的一些个人理解

    背景 由于最近在准备换工作,所以开始补充一些基础知识,以前准备的时候总是去硬背一些知识点,这次花了不少时间去问了问为什么,年前对于幻读的内容有了点心得,为了不遗忘,也是为了只有能讲出来才算是真的理解了 ...

  2. 数据库基础知识ACID,隔离级别RC,RR,RU,SERIALIZABLE,Phantom Rows幻读,解决幻读,脏读dirty read

    ACID A atomicity, C consistency,I isolation, and D durability的缩写,这些特性和事务是紧密联系的,InnoDB事务的特点和ACID原则紧密联 ...

  3. mysql 什么是幻读_何为幻读?MySQL又是如何解决幻读的?

    一.什么是幻读 在一次事务里面,多次查询之后,查询的结果集的个数不一致的情况叫做幻读.而多出来或者少的哪一行被叫做 幻行 二.为什么要解决幻读 在高并发数据库系统中,需要保证事务与事务之间的隔离性,还 ...

  4. mysql 面试知识点笔记(七)RR如何避免幻读及非阻塞读、范式

    2019独角兽企业重金招聘Python工程师标准>>> 表象:快照读(非阻塞读)--伪MVCC (Multi-Version Concurrent Controll多版本并发控制) ...

  5. 事务隔离级别——未提交读、已提交读、可重复读、串行

    事务隔离级别--未提交读.已提交读.可重复读.串行 事务隔离级别是指多个事务之间,不同事务中涉及的读写操作互相影响的隔离.其中多个事务中同时对同一条数据或者表进行写操作(insert.update.d ...

  6. MySQL怎么运行的系列(十一)快照读、锁定读、半一致性读 和 加锁语句分析

    本系列文章目录 展开/收起 MySQL怎么运行的系列(一)mysql体系结构和存储引擎 MySQL怎么运行的系列(二)Innodb缓冲池 buffer pool 和 改良版LRU算法 Mysql怎么运 ...

  7. java锁(公平锁和非公平锁、可重入锁(又名递归锁)、自旋锁、独占锁(写)/共享锁(读)/互斥锁、读写锁)

    前言 本文对Java的一些锁的概念和实现做个整理,涉及:公平锁和非公平锁.可重入锁(又名递归锁).自旋锁.独占锁(写)/共享锁(读)/互斥锁.读写锁 公平锁和非公平锁 概念 公平锁是指多个线程按照申请 ...

  8. CoLoRMap: Correcting Long Reads by Mapping short reads CoLoRMap:通过映射短读来纠正长读

    CoLoRMap: Correcting Long Reads by Mapping short reads CoLoRMap:通过映射短读来纠正长读 Motivation: 第二代测序技术为测序基因 ...

  9. java:1221是一个非常特殊的数,它从左边读和从右边读是一样的,编程求所有这样的四位十进制数。

    资源限制 时间限制:1.0s 内存限制:512.0MB 问题描述 1221是一个非常特殊的数,它从左边读和从右边读是一样的,编程求所有这样的四位十进制数. 输出格式 按从小到大的顺序输出满足条件的四位 ...

  10. java imap 标记已读,JavaMail通过IMAP和POP3接收未读以及设置已读邮件

    JavaMail通过IMAP和POP3接收未读以及设置已读邮件 博客分类: javamail javamailpop3imap 使用javaMail收邮件主要有两种协议,一种是pop3,一种是imap ...

最新文章

  1. 利用Mircosoft URLRewriter.dll实现asp.net页面伪静态
  2. MQTT protocol level的处理
  3. 【WPF】右键菜单ContextMenu可点击区域太小的问题
  4. 创造包容的环境和上升空间
  5. asp.net页面中hmtl注释的问题
  6. iOS开发ARC入门和使用
  7. Centos修改主机名,重启后无效解决
  8. java netty post_API调用Netty长链接执行发送消息(在线数、用户列表)
  9. android 33 对话框控件
  10. 全国计算机二级模拟考试软件,全国计算机等级考试模拟考试软件
  11. 模拟电路电源芯片PROTUES
  12. 安装mysql不是Mariadb_解决centos7 中 使用mariadb 安装mysql不成功的问题
  13. Ubuntu20.04使用FreeRADIUS搭建EAP认证环境
  14. 安装GitExtentions KDiff3已配置为合并工具,kdiff3的路径未配置
  15. 如何在树莓派上进行python编程_树莓派Python编程指南
  16. 【聚来宝】创业 兼职 教程 资料
  17. 端口汇聚实现多端口带宽叠加
  18. PS绘制飘逸彩色丝带教程
  19. 【Excel神技】之 快速填充
  20. 2013年互联网江湖格局观

热门文章

  1. Reactor模式详解
  2. 【大厂高频真题100题】《整数转罗马数字》 真题练习第21题 持续更新~
  3. java字符串的比较(区分大小写、忽略大小写)
  4. 吕布机器人评测_入手评测腾讯态客王者荣耀吕布机器人普通版好用吗?怎么样呢?用户吐槽曝光...
  5. linux i3音量、亮度调节
  6. windows搭建NTP时钟服务器(win xp、7、8、10)
  7. mysql java中文乱码_java连接mysql添加中文乱码_MySQL
  8. python对ip地址归属地查询笔记
  9. 【雪花图案】AI+ASS绘图代码分享
  10. 教你批量查询多个退回件快递单号的物流信息