当前位置: 首页 > news >正文

可重复读 是否“100%”地解决幻读?

这是一个非常深刻的问题,答案是:几乎解决了,但在一个非常特殊且罕见的边界场景下,理论上仍然可能出现幻读。 因此,严格来说,它并非被“彻底”或“100%”地解决。

下面我们来详细分解这个结论:

1. InnoDB 如何“几乎”解决了幻读?

正如之前讨论的,InnoDB 通过两种强大的武器来攻击幻读问题:

  • 对于快照读(Snapshot Read):即普通的 SELECT 语句。通过 MVCC(多版本并发控制),事务看到的是一个在它开始时创建的、静态的数据快照。无论其他事务如何插入、删除或更新,这个快照都不会改变。因此,在同一个事务内,多次执行相同的 SELECT 查询,结果集的行数绝对是一致的。这完全消除了快照读下的幻读。
  • 对于当前读(Current Read):即加锁的 SELECT ... FOR UPDATE / SELECT ... LOCK IN SHARE MODE 以及 UPDATEDELETE 语句。通过 间隙锁(Gap Lock)和临键锁(Next-Key Lock),InnoDB 不仅锁定了已有的记录,还锁住了记录之间的“间隙”,防止其他事务在这个范围内插入新的数据。这防止了其他事务的插入操作导致当前事务的当前读出现幻读

基于这两种机制,在99.9%的应用场景下,你可以认为InnoDB的可重复读隔离级别已经解决了幻读。这也是它成为MySQL默认隔离级别并能支撑绝大多数高并发业务的底气所在。

2. 那个“不彻底”的边界场景是什么?

理论上的漏洞出现在:一个事务先进行当前读(从而受间隙锁保护),然后在其内部进行快照读

让我们看一个经典的例子:

表结构:

CREATE TABLE `accounts` (`id` int(11) PRIMARY KEY,`name` varchar(50),`balance` int(11)
);
INSERT INTO accounts VALUES (1, 'Alice', 100), (5, 'Bob', 200);
-- 注意:id 2, 3, 4 目前不存在,这些就是“间隙”。

事务A (T1)

事务B (T2)

START TRANSACTION;

SELECT * FROM accounts WHERE id > 1 FOR UPDATE;
-- 这是一个当前读。它会锁定id>1的所有现有记录(id=5)和所有间隙(防止插入id=2,3,4等)。
结果: (5, 'Bob', 200)

START TRANSACTION;
INSERT INTO accounts VALUES (3, 'Charlie', 300);
-- 此操作会被事务A的间隙锁阻塞,无法完成!

SELECT * FROM accounts WHERE id > 1;
-- 这是一个快照读。MVCC保证它看不到其他事务未提交的更改,所以结果和第一次一样。
结果: (5, 'Bob', 200)

COMMIT;
-- 提交并释放锁

-- 事务A的锁释放后,事务B的INSERT操作立即成功执行。

COMMIT;

到目前为止,一切正常,幻读被成功防止。


现在,让我们制造那个边界场景:

事务A (T1)

事务B (T2)

START TRANSACTION;

SELECT * FROM accounts WHERE id > 1 FOR UPDATE;
结果: (5, 'Bob', 200)

START TRANSACTION;
INSERT INTO accounts VALUES (3, 'Charlie', 300);
-- 同样被阻塞

...

...

-- 关键一步:事务A自己执行一个插入操作,这个操作恰好也落在被它锁住的间隙里

INSERT INTO accounts VALUES (2, 'David', 400);
-- 这个操作是允许的!一个事务总是可以修改自己被锁住的数据。

-- 此时,由于事务A执行了DML操作(INSERT),InnoDB会隐式地推进它的快照时间点(在某些版本和场景下),以保证事务自身能看到自己刚做的修改。

SELECT * FROM accounts WHERE id > 1;
-- 再次进行快照读。此时快照可能已经被更新,它不仅能看到自己刚插入的id=2的记录,也可能看到之前被它阻塞、但现已提交的事务B插入的id=3的记录!

结果: (2, 'David', 400), (3, 'Charlie', 300), (5, 'Bob', 200)

COMMIT;

分析:
在同一个事务A内,两次执行 SELECT ... WHERE id > 1

  • 第一次返回 1 行。
  • 第二次返回 3 行。
  • 行数发生了变化,这符合幻读的定义。

结论

  1. 是否彻底解决? 。从理论和技术完备性的角度,InnoDB的可重复读隔离级别存在一个极其罕见的边界场景(自身DML操作推进快照并看到其他已提交的插入),使得幻读仍然可能发生。
  2. 是否值得担心? 几乎不需要。这个场景需要非常特殊的操作序列(先加锁读,然后自己或他人恰好操作同一个间隙,最后自己再读),在绝大多数真实业务逻辑中几乎不会有意或无意地这样编写代码。
  3. 实践中的选择? 你可以放心地将InnoDB的可重复读隔离级别视为解决了幻读问题。如果您的应用处于那0.1%的极端场景且对一致性有极致要求,解决方案通常是:
    • 使用串行化(SERIALIZABLE)隔离级别:彻底解决,但性能代价最高。
    • 在需要绝对精确的地方显式使用 SELECT ... FOR UPDATE:通过持续加锁来保证当前读的一致性。


文章转载自:

http://4UXL8FOV.qjsxf.cn
http://hlUfaGn0.qjsxf.cn
http://2ZZ8bOI3.qjsxf.cn
http://Yyvxru3y.qjsxf.cn
http://lDwxMD3T.qjsxf.cn
http://HjvLjE5H.qjsxf.cn
http://juoN3KzU.qjsxf.cn
http://7Kv3TKvh.qjsxf.cn
http://Bv6PsVR6.qjsxf.cn
http://w67reggZ.qjsxf.cn
http://BHOYbilm.qjsxf.cn
http://rJz0Btte.qjsxf.cn
http://3SGmajqO.qjsxf.cn
http://TphtXq5R.qjsxf.cn
http://vm8lgKAH.qjsxf.cn
http://B6TjgUIM.qjsxf.cn
http://684uq3xI.qjsxf.cn
http://ni3UqyTa.qjsxf.cn
http://oLt5KW3H.qjsxf.cn
http://c4C12moI.qjsxf.cn
http://tkI8fbJR.qjsxf.cn
http://tGPARzxB.qjsxf.cn
http://7mWdODmB.qjsxf.cn
http://GJLi0MB4.qjsxf.cn
http://UJhQ033p.qjsxf.cn
http://Uzj5w2Lv.qjsxf.cn
http://jeuRl9kM.qjsxf.cn
http://gCjV3Q4c.qjsxf.cn
http://X76H7SwV.qjsxf.cn
http://LllkIfYT.qjsxf.cn
http://www.dtcms.com/a/371071.html

相关文章:

  • 数据结构与算法1 第一章 绪论
  • Unity的UGUI更改背景以及添加中文字体
  • Linux网络接口命名详解:从eth0到ens33
  • C++零基础第四天:顺序、选择与循环结构详解
  • 南科大适应、协同与规划的完美融合!P³:迈向多功能的具身智能体
  • 机床夹具设计 +选型
  • 【开题答辩全过程】以 “爱心”家政管理系统为例,包含答辩的问题和答案
  • LCR 175. 计算二叉树的深度【简单】
  • SPI 三剑客:Java、Spring、Dubbo SPI 深度解析与实践​
  • 人工智能辅助荧光浓度检测系统:基于YOLO与RGB分析的Python实现
  • Netty从0到1系列之EventLoopGroup
  • 简说【高斯随机场 (GRF)】
  • 【黑客技术零基础入门】2W字零基础小白黑客学习路线,知识体系(附学习路线图)
  • Altium Designer(AD24)集成开发环境简介
  • C++协程理解
  • 【科研成果速递-IJGIS】如何描述与分类移动对象的时空模式?一个新的分类框架与体系!
  • idf--esp32的看门狗menuconfig
  • 「数据获取」《中国电力统计年鉴》(1993-2024)(含中国电力年鉴)
  • [光学原理与应用-435]:晶体光学 - 晶体的结构-基元/原胞/晶胞/点阵
  • 郭平《常变与长青》读书笔记(第三章)
  • C++_哈希
  • 【LeetCode 热题 100】49. 字母异位词分组
  • Vue3中Vite的介绍与应用
  • 【C++ 11 模板类】tuple 元组
  • 高维前缀和
  • 脑电数据预处理十四:主成分分析(PCA)用于伪迹去除与降维
  • 09-FreeRTOS任务调度
  • 详解 ELO 评分系统
  • TDengine 时间函数 WEEKDAY() 用户手册
  • 计算机网络:物理层---数据通信基础知识