🌈🌈🌈🌈🌈🌈🌈🌈
欢迎关注公众号(通过文章导读关注:【11来了】),及时收到AI 前沿项目工具及新技术
的推送
发送资料
可领取深入理解 Redis 系列文章结合电商场景讲解 Redis 使用场景
、中间件系列笔记
和编程高频电子书
!文章导读地址:点击查看文章导读!
感谢你的关注!
🍁🍁🍁🍁🍁🍁🍁🍁
事务中的 ACID 特性
是必须要知道:
MySQL 的 事务隔离级别
有 4 种:
可重复读是 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 提交的新增数据在可重复读隔离级别中,通过 临键锁
在一定程度上缓解了幻读的问题,但是在特殊情况下,还是会出现幻读
以下两种情况下,会出现 幻读
,大家可以先看一下如何出现的幻读, 思考一下为什么会出现幻读
,答案会写在后边!
对于下图中的执行顺序,会出现幻读现象,可以看到在事务 A 执行到第 7 行发现查询到了事务 B 新提交的数据了
这里都假设使用的 InnoDB 存储引擎,事务隔离级别默认都是 可重复读
在可重复读隔离级别下,使用了 MVCC 机制,select 操作并不会更新版本号,是快照读(历史版本),执行 insert、update 和 delete 时会更新版本号,是当前读(当前版本),因此在事务 A 执行了第 6 行的 update 操作之后,更新了版本号,读到了 id = 5 这一行数据的最新版本,因此出现了幻读!
对于下边这种情况也会出现幻读,在第 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 这条数据,就会因为有锁的存在,阻塞插入语句