【面试突击】数据库面试实战(下)

发布时间:2024年01月17日

🌈🌈🌈🌈🌈🌈🌈🌈
欢迎关注公众号(通过文章导读关注:【11来了】),及时收到 AI 前沿项目工具及新技术 的推送
发送 资料 可领取 深入理解 Redis 系列文章结合电商场景讲解 Redis 使用场景中间件系列笔记编程高频电子书

文章导读地址:点击查看文章导读!

感谢你的关注!

🍁🍁🍁🍁🍁🍁🍁🍁

事务基础

事务中的 ACID 特性 是必须要知道:

  • Atomic:原子性,一组 SQL 要么同时成功,要么同时失败
  • Consistency:一致性,保证执行完 SQL 之后数据是准确的
  • Isolation:隔离性,多个事务之间不会互相干扰
  • Durability:持久性,事务提交之后,可以保证对数据库所作的更改是永久性的

事务的隔离级别

MySQL 的 事务隔离级别 有 4 种:

  • 读未提交:事务 A 会读取到事务 B 更新但没有提交的数据。如果事务 B 回滚,事务 A 产生了脏读
  • 读已提交:事务 A 会读取到事务 B 更新且提交的数据。事务 A 在事务 B 提交前后两次查询结果不同,产生不可重复读
  • 可重复读:保证事务 A 中多次查询数据一致。可重复读是 MySQL 的默认事务隔离级别。可重复读可能会造成幻读 ,事务A进行了多次查询,但是事务B在事务A查询过程中新增了数据,事务A虽然查询不到事务B中的数据,但是可以对事务B中的数据进行更新
  • 可串行化:并发性能低,不常使用

这一部分需要了解的就是每一种隔离级别可能会带来的问题,如下这个表格所示:

隔离级别脏读(Dirty Read)不可重复读(NonRepeatable Read)幻读(Phantom Read)
未提交读(Read uncommitted)可能可能可能
已提交读(Read committed)不可能可能可能
可重复读(Repeatable read)不可能不可能可能
可串行化(Serializable )不可能不可能不可能

那么肯定就要了解 脏读不可重复读幻读 到底是个什么东东?

  • 脏写:多个事务更新同一行,每个事务不知道其他事务的存在,最后的更新覆盖了其他事务所做的更新
  • 脏读:事务 A 读取到了事务 B 已经修改但是没有提交的数据,此时如果事务 B 回滚,事务 A 读取的则为脏数据
  • 不可重复读:事务 A 内部相同的查询语句在不同时刻读出的结果不一致,在事务 A 的两次相同的查询期间,有其他事务修改了数据并且提交了
  • 幻读:当事务 A 感知到了事务 B 提交的新增数据

幻读问题

在可重复读隔离级别中,通过 临键锁 在一定程度上缓解了幻读的问题,但是在特殊情况下,还是会出现幻读

以下两种情况下,会出现 幻读,大家可以先看一下如何出现的幻读, 思考一下为什么会出现幻读 ,答案会写在后边!

  • 情况1:事务 A 通过更新操作获取最新视图之后,可以读取到事务 B 提交的数据,出现幻读现象

对于下图中的执行顺序,会出现幻读现象,可以看到在事务 A 执行到第 7 行发现查询到了事务 B 新提交的数据了

这里都假设使用的 InnoDB 存储引擎,事务隔离级别默认都是 可重复读

在可重复读隔离级别下,使用了 MVCC 机制,select 操作并不会更新版本号,是快照读(历史版本),执行 insert、update 和 delete 时会更新版本号,是当前读(当前版本),因此在事务 A 执行了第 6 行的 update 操作之后,更新了版本号,读到了 id = 5 这一行数据的最新版本,因此出现了幻读!

在这里插入图片描述

  • 情况2:事务 A 在步骤 2 执行的读操作并不会生成间隙锁,因此事务 B 会在事务 A 的查询范围内插入行

对于下边这种情况也会出现幻读,在第 6 行使用 select ... for update 进行查询,这个查询语句是当前读(查询的最新版本),因此查询到了事务 B 新提交的数据,出现了幻读!

在这里插入图片描述

那么对于以上两种情况来说,为什么会出现幻读呢?

对于事务 A 出现了幻读,原因就是,事务 A 执行的第 2 行是普通的 select 查询,这个普通的 select 查询是快照读,不会生成临键锁(具体生成临键锁、记录锁还是间隙锁根据 where 条件的不同来选择),因此就 不会锁住这个快照读所覆盖的记录行以及区间

那么事务 B 去执行插入操作,发现并没有生成临键锁,因此直接可以插入成功

重要:那么我们从代码层面尽量去避免幻读问题呢?

在一个事务开始的时候,尽量先去执行 select ... for update,执行这个当前读的操作,会先去生成临键锁,锁住查询记录的区间,不会让其他事务插入新的数据,因此就不会产生幻读

这里我也画了一张图如下,你也可以去启动两个会话窗口,连接上 mysql 执行一下试试,就可以发现,当事务 A 执行 select ... for update 操作之后,就会加上临键锁(由于 where 后的条件是 id=5,因此这个临键锁其实会退化为记录锁,将 id=5 这一行的数据锁起来),那么事务 B 再去插入 id=5 这条数据,就会因为有锁的存在,阻塞插入语句

在这里插入图片描述

文章来源:https://blog.csdn.net/qq_45260619/article/details/135650753
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。