MySQL 单点详解(08)

MySQL 单点详解(08):RR 到底解决幻读没有,快照读和当前读各算一半

面试官:RR 隔离级别下,幻读解决了吗?

面试官:为什么加了 for update,就能挡住别的事务插进来?

幻读不是一个问题,是两个问题。

快照读看到的是历史,当前读看到的是现实,两套解法,混着答就会自相矛盾。

一、幻读到底长什么样

同一个事务里,同样的范围查询执行两次,第二次多出来几行,这就是幻读。

多出来的行,是别的事务插进去、并且已经提交的。

比如第一次查 id 大于 3 得到 3 行,别的事务插入一行 id 是 9 并提交,同样的语句再查一次,成了 4 行。

多出来的不是被改出来的,是被插出来的。这就是它和不可重复读的区别。

难点在于:想拦住插入,得先知道"哪里还没有行"。区间里的空隙,就是那个"哪里"。

二、先把读分成两种

表① 同样是 select,两种命

读法 长什么样 读到的版本
快照读 普通 select 事务开始那一刻的历史
当前读 for update、update、delete 最新的、已提交的那一版

这个划分比什么都重要。幻读的题答歪,八成是把这两种读混成了一种。

快照读走的是第六期那条版本链,读的是一份固定的历史。

别人后来插的行,事务号比快照上限还大,一看就不可见,压根不会被返回。

所以快照读根本不需要间隙锁,它天生就看不见新行。

三、当前读才需要间隙锁

当前读要拿最新数据,就不能靠历史挡着。它得让"插入"这件事压根发生不了。

表② 三种读,会不会看到别人刚插的行

读法 看得到吗 靠什么挡
普通 select 看不到 不需要挡
for update 看得到,所以要先挡 间隙锁挡住插入
update 看得到,所以要先挡 间隙锁挡住插入

于是就有了那个经典场景:

T1 执行 select * from t where id = 5 for update,查不到,但这个区间被锁上了。

id 等于 5 这一行并不存在,锁的不是行,是位置。空隙被锁住,插入必须落在这段空隙里,所以只能等。

T2 想插 id = 5,撞在间隙锁上,只能等。

T1 再查一次,还是空。没有幻读。

如果 T1 那句不带 for update,它是快照读,本来也看不到,但 T2 的插入它拦不住。

所以稳妥的写法是在事务开头就用加锁读把范围占住,而不是先查一遍、再决定要不要锁。后者中间那段空隙,别人随时能插。

四、RR 也不是完全免疫

这是最容易被追问的一层。看一个真实会发生的顺序。

表③ 四步,最后多出一行

步骤 结果
事务先做一次范围快照读 3 行
别的事务插入一行并提交 仍是 3 行
本事务 update 整个范围 影响 4 行
再快照读一次 4 行

第三步是关键。update 是当前读,它看见了别人刚插的那一行,并且把这行的事务号改成了自己。

第四步再走版本链时,这行的事务号等于自己,判定为可见,于是冒出来了。

所以严格说:RR 解决的是"快照读的幻读",不是"所有幻读"。

网上说"RR 完全解决幻读",答得太满,遇到这种追问就圆不回来。

五、面试官会往下问的三层

第一层:RC 为什么不行?

RC 每条语句都重建快照,同一事务两次读之间就可能变。

它也没有间隙锁,插入拦不住。两条路都断,所以 RC 一定有幻读。

第二层:那 RC 有什么好处?

间隙锁只在 RR 下用,锁的范围大、并发低。RC 不用,冲突少、死锁也少。

很多互联网业务主动选 RC,用应用层去处理那点不一致的公差。

第三层:既然快照读没问题,为什么还要间隙锁?

因为不是所有业务都能忍受"读到的是历史"。

扣库存、转账这类操作必须拿最新值,一拿最新值就回到当前读,就得靠锁。

很多系统干脆在应用层再加一个版本号,冲突了就让重试,用乐观锁兜住这一层。

收尾

这期能压成一句:快照读靠版本链躲开新行,当前读靠间隙锁堵住新行,两条路合起来才叫"RR 基本解决幻读"。

相关推荐
thefool1122662 小时前
HTTPS协议
java
acd120092 小时前
Nginx 反向代理的缓冲区坑:后端响应大时接口偶发 502
java·运维·nginx
装不满的克莱因瓶2 小时前
支付订单成功异步MQ权益发放设计和开发
java·架构·系统架构·rabbitmq·硬件架构
高冷小鱼殿下2 小时前
内部类Lambda与枚举
java·开发语言
l1t2 小时前
DeepSeek总结的如何在 PostgreSQL 中删除一张 4 TB 的表
数据库·postgresql
ClouGence2 小时前
开源数据库管理工具 CloudDM 发布新版本,支持团队多级审批工单,无需依赖第三方系统
数据库·sql·开源
Thanks_ks3 小时前
常见 HTTP 状态码的坑:301/302、401/403、500/502 之间差的那一个词
计算机网络·nginx·http·面试·状态码·后端开发·浏览器缓存
怕浪猫3 小时前
Super-agent 在各大模型厂商的应用情况
算法·面试·github
小蒜学长3 小时前
编程语言在线学习平台的设计与实现(代码+数据库+LW)
java·spring boot·后端·编程语言在线学习平台·在线编码