第二十章 幻读是什么,幻读有什么问题?

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);
  • 下面的语句序列,是怎么加锁的,加的锁又是什么时候释放的呢 ?
begin;
select * from t where d=5 for update;
commit;
  • select ... for update

    • 对于 非索引字段 进行 updateselect .. for update 操作,代价极高。所有记录上锁,以及所有间隔的锁
    • 对于 索引字段 进行上述操作,代价一般。只有 索引字段 本身和 附近的间隔 会被加锁
    • select ... for update 是为了在查询时,对这条数据进行加锁,避免其他用户以该表进行插入、修改或删除等操作,造成表的不一致性

关于 “这个语句会命中 d=5 的这一行,对应的主键 id=5,因此在 select 语句执行完成后,id=5 这一行会加一个写锁,而且由于两阶段锁协议,这个写锁会在执行 commit 语句的时候释放” 这句话的理解

由于字段 d 上没有索引,因此这条查询语句会做全表扫描。那么,其他被扫描到的,但是不满足条件的 5 行记录上,会不会被加锁呢?

  • RR (可重复读) 级别:扫描到的数据都会加 行锁 和 间隙锁
  • RC (读提交) 级别:扫描到的数据都会加行锁,但是不满足条件的数据,没有到 commit 阶段,就会被释放,违反了两阶段加锁原则
  • 全表扫描一直指的是扫描主键索引

什么是幻读 ?

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

share in mode:当前读,给数据加读锁

for update:当前读,给数据加写锁

什么是幻读 ?

  • 在同一个事务中,两次读取到的数据不一致的情况,称为 幻读不可重复读
  • 幻读是针对 insert 导致的数据不一致
  • 不可重复读是针对 deleteupdate 导致的数据不一致
  • 可重复读 隔离级别下,事务内查询用 “当前读”,读到本事务外新增的数据,称为 幻读
  • 幻读 是用户在使用 可重复读 隔离级别下,在进行 select 查询时加 IX锁、IS锁才可能出现的,是用户主动打破业务层面上的查询时的一致性视图隔离性
  • 其不属于事务隔离的可见性规则问题(可见性规则是来解决各种事务问题的),而是用户在使用上出现的问题,属于业务问题

什么是快照读 ? 什么是当前读 ?

  • 当前读指的是 select for update 或者 select in share mode ,指的是在更新之前必须先查询当前的值,因此叫当前读
  • 快照读指的是在语句执行之前或者在事务开始的时候会创建一个视图,后面的读都是基于这个视图的,不会再去查询最新的值

当前读 具体包括哪些操作 ?

  • for updatelock in share modeupdatedeleteinsert 都是当前读的规则,就是读取最新的已经提交的数据
  • update 先查询再修改,这里的查询就是使用的当前读
  • delete 要先查询再删除,这里的查询也是要当前读
  • insert 的时候,要判断主键是否已经存在、是否违反唯一约束,此时查看主键是否存在的查询也是当前读

幻读 和 脏读 的区别 ?

  • 幻读是读到了提交了的数据,而脏读是读到了没提交的脏数据
  • 读提交 的隔离级别下,没有讨论幻读的实际意义
  • 而在 可重复读 隔离级别下,当前读 打破了视图的隔离限制,实现了读到不应该读的数据的作用

可重复读 的隔离级别下,幻读只会在查询为哪种性质时才会出现 ?

  • 由于 一致性视图 的作用,因此幻读只会在 “当前读” 情况下发生

幻读 跟 事务的可见性规则 冲突吗 ?

  • 可重复读 隔离级别下,幻读是用户选择使用 当前读 而产生的,符合 当前读 的规则,也不跟事务的 可见性规则 相矛盾

幻读有什么问题 ?

select .. lock in share modeselect ... for update 的区别 ?

  • select .. lock in share mode 走的是 IS锁(意向锁)

    • 即在符合条件的 rows 上都加了共享锁,这样的话,其他 session 可以读取这些记录,也可以继续添加 IS 锁,但是无法修改这些记录直到你这个加锁的 session 执行完成 (否则直接锁等待超时)
  • select ... for update 走的是 IX锁 (意向排它锁)
    • 即在符合条件的 rows 上都加了排它锁,其他 session 也就无法在这些记录上添加任何的IS锁IX锁
  • 如果不存在 一致性非锁定读的话,那么其他 session 是无法读取和修改这些记录的
  • 但是 innoDB 存在 非锁定读 (快照读并不需要加锁),for update 之后并不会阻塞其他 session 的快照读取操作
  • 除了 select ...lock in share modeselect ... for update 这种显示加锁的查询操作
  • 通过对比,发现 for update 的加锁方式无非是比 lock in share mode 的方式多阻塞了select...lock in share mode 的查询方式,并不会阻塞快照读

幻读 产生的原因 ?

  • 注意:binlog 日志是在 commit 提交时才进行记录的
  • 即使给所有行加上了锁,也避免不了幻读
  • 这是因为给行加锁的时候,这条记录还不存在,没法加锁
  • 例如下面这种情况:假设扫描到的行都被加上了 行锁

  • 由于 session A 把所有的行都加了写锁,所以 session B 在执行第一个 update 语句的时候就被锁住了,需要等到 T6 时刻 session A 提交以后,session B 才能继续执行
  • binlog 里面,执行序列是这样的:
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*/update t set d=5 where id=0; /*(0,0,5)*/
update t set c=5 where id=0; /*(0,5,5)*/
  • 按照日志顺序执行,id=0 这一行的最终结果也是 (0,5,5)。所以,id=0 这一行的问题解决了
  • 但是你可以看到,id=1 这一行,在数据库里面的结果是 (1,5,5),而根据 binlog 的执行结果是 (1,5,100),也就是说 幻读 的问题还是没有得到解决
    • 原因是:在 T3 时刻,我们给所有行加锁的时候,id=1 这一行还不存在,不存在也就加不上锁

如何解决幻读 ?

如何解决 幻读 ?

  • InnoDB 在 行锁 的基础上,引入了 间隙锁 (Gap Lock)
  • 间隙锁,锁的就是两个值之间的空隙
  • 比如文章开头的表 t,初始化插入了 6 个记录,这就产生了 7 个间隙
insert into t values(0,0,0),(5,5,5),
(10,10,10),(15,15,15),(20,20,20),(25,25,25);

  • MySQL 会对扫描经过的索引对象,加上间隙锁

行锁 和 行锁 之间会产生冲突,那 间隙锁 和 间隙锁 之间,也会产生冲突吗 ?

  • 行锁分为:读锁、写锁

  • 行锁:读锁之间不冲突写锁与读锁冲突写锁与写锁冲突
  • 但是,跟间隙锁存在冲突关系的,是 “往这个间隙中插入一个记录” 这个操作
  • 间隙锁 与 间隙锁 之间,都不存在冲突关系

什么是 next-key lock ?

  • 间隙锁行锁 合称 next-key lock,每个 next-key lock前开后闭区间
  • 如果用 select * from t for update 要把整个表所有记录锁起来,就形成了 7 个 next-key lock,分别是 (-∞,0](0,5](5,10](10,15](15,20](20, 25](25, +supremum]
  • 间隙锁只是锁间隙,没有锁住记录行,而 next-key lock 就是间隙锁基础上锁住右边界行
  • 间隙锁:( ) 开区间
  • next-key lock:( ] 前开后闭区间
  • 如果是表的最后一行数据,则下一个区间是这行数据到表索引的不存在最大值,也就是 +supremum

引入 间隙锁 后,可能会导致什么问题 ?

  • 首先需要了解 间隙锁 的特性:锁的是间隙,是禁止其他操作往间隙中插入数据的。因此间隙锁与间隙锁之间不冲突
  • 所以在 AB 两个事务当中
  • A事务 随机查询某一条数据(+排它锁,也就是 for update )的方式产生间隙锁
  • B事务 也执行同一条SQL,随后 A事务 判定数据不存在时,对其插入,B事务也同样如此操作,则最终导致 A事务 等待 B事务 的间隙锁,B事务 也在等待 A事务 的间隙锁,这将导致死锁
  • 当然,由于 InnoDB 有开启死锁检测,最后 A事务 报错返回
  • 但是,间隙锁 会影响数据的并发度

除了引入 间隙锁, 还有什么方式可以解决幻读 ?

  • RR (可重复读) 改为 RC (读提交),则不存在幻读问题
  • 但此时需要将 binlog 格式改为 row,否则可能出现 数据 和 日志 不一致的问题

为什么 RC (读提交) 下,需要将 binlog 格式改为 row 呢 ?

  • RC 没有幻读问题,也没有间隙锁,但在显示控制事务更新
  • 若在 事务A 还未提交之前,有别的 事务B 后执行并且添加的数据是 事务A 的更新语句逻辑层面会命中的条件
  • 那等 事务A 提交之后,statement 的 binlog 记录上,事务B的记录在前,而事务A的记录在后(这没毛病,谁先提交谁先记录,但恢复的时候会产生问题)
  • 当执行日志恢复时,后提交的 事务A 的记录会把 事务B 的记录也进行更新,这就导致了数据恢复错误
  • 此时,如果改成 row 格式,binlog 会具体记录语句的各项条件,这样在恢复时便不会恢复错误了
  • 注意:除了恢复,还有主从库的同步也会有这个问题
  • 举个例子:
  • 例子一:
  • 删除 statement 记录的是这个删除的语句
delete from t where age>10 and modified_time<='2020-03-04' limit 1;
  • row 格式记录的是实际受影响的数据是真实删除行的主键id
delete from t where id=3 and age=12 and modified_time='2020-03-05';
  • 例子二:
-- statemnt格式
begin;
update t set d=5 where id=0;
commit;-- row格式
begin;
update t where id=0 and c=0 and d=0
set id=0,c=0,d=5
commit;

关于row格式的参考资料

为什么要把MySQL的binlog格式修改为row

大家都用读提交,可是逻辑备份的时候,mysqldump 为什么要把备份线程设置成可重复读呢 ?

  • 官方自带的逻辑备份工具是 mysqldump
  • mysqldump 使用参数 –single-transaction 的时候,导数据之前就会启动一个事务,来确保拿到一致性视图
  • 因为 RR (可重复读) 的一致性读视图可以保证 数据备份时,不阻塞其他数据写入

在备份期间,备份线程用的是可重复读,而业务线程用的是读提交。同时存在两种事务隔离级别,会不会有问题 ?

  • 没有问题,不管是 读提交 还是 可重复读,都是 MVCC 支持
  • 备份是从某个快照时间之后开始的,数据是固定一致准确的

一条加了 排它锁 的查询语句,如果查询是全表扫描,那么扫描过的语句会如何 ?

  • 可重复读 隔离级别下,MySQL 会对扫过的语句加 next-key lock

总结

  • 重复读 更新时采用 当前读,这是为了防止数据丢失
  • 但使用 当前读,可能造成更新层面语义的破坏日志数据 不一致的问题
  • 为解决更新层面幻读 问题,可重复读时,引入了 next-key lock
  • 若是直接采取 读提交,则解决了语义层面的破坏,但需要通过将 binlog 改为 maxrow 的形式,防止日志数据不一致

第二十章 幻读是什么,幻读有什么问题?相关推荐

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

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

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

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

  3. activiti高亮显示图片_【正点原子FPGA连载】第二十章SD卡读BMP图片HDMI显示实验领航者 ZYNQ 之嵌入式开发指南...

    1)实验平台:正点原子领航者ZYNQ开发板 2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761 3)全套实验源码+手册+视频下 ...

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

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

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

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

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

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

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

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

  8. 鸟哥的Linux私房菜(基础篇)- 第二十章、启动流程、模块管理与 Loader

    第二十章.启动流程.模块管理与 Loader 最近升级日期:2009/09/14 系统启动其实是一项非常复杂的程序,因为核心得要侦测硬件并加载适当的驱动程序后,接下来则必须要呼叫程序来准备好系统运行的 ...

  9. 【正点原子Linux连载】第二十章 V4L2摄像头应用编程-摘自【正点原子】I.MX6U嵌入式Linux C应用编程指南V1.1

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  10. 【正点原子FPGA连载】 第二十章 LCD触摸屏实验摘自【正点原子】DFZU2EG/4EV MPSoC 之FPGA开发指南V1.0

    1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=692450874670 3)全套实验源码+手册+视频下载地址: h ...

最新文章

  1. 计算机仿真和vr的区别,AR对比VR有何不同?你能分清楚他们的区别么
  2. NUMTRYE - Number Theory (Easy)
  3. android手机 scala环境,在Android中使用Scala中的Java常量
  4. python查询缺失值所在位置_Python Pandas找到缺失值的位置方法
  5. CANN AICPU算子耗时分析及优化探索
  6. js 按给定数组的顺序给数组排序_JavaScript中如何如何给数组以及数组对象根据value值进行排序。...
  7. FTP:226 transfer done but failed to open directory
  8. spring-cloud-starter-openfeign使用详解
  9. 关于水题POJ2159
  10. DreamScene for Windows 7梦幻桌面使用教程
  11. 【机器学习】K近邻(KNN)算法详解
  12. 平安夜关于苹果的题目——1705. 吃苹果的最大数目
  13. 计算机编程入门先学什么最好?
  14. C语言—俄罗斯方块(新手向)
  15. 密码箱忘记密码解决方法
  16. native-JavaScript通信流程( 基于最新的 react native版本 )
  17. 虚拟桌面:一个简单的桌面管理工具
  18. 惊闻同事噩耗,思绪联翩
  19. python gui tkinter_Python GUI tkinter 学习笔记(一)
  20. 琢石成器之自动化去广告神器

热门文章

  1. 关于电视剧评价指标的描述性分析
  2. 计算机海报大赛策划书,海报策划书模板.docx
  3. 程序员有哪些职业发展方向呢?Linux运维
  4. 学java进美团_美团实习Java岗面经,已拿offer
  5. [论文写作笔记] C9 概括和结论展示科学严谨性
  6. thinkphp 3.2引入第三方类
  7. 随机效应模型介绍及实例分析
  8. 关于Linux查询ip地址时出现乱码
  9. PowerShell输出IP地址
  10. iphonex 序列号_X的序列号什么开头有什么意思吗?