数据库MySQL——MySQL 可重复读隔离级别:Read View 底层原理与幻读问题深度剖析(面试必知)

目录

[一、 三大并发读问题](#一、 三大并发读问题)

[二、 SQL 标准定义的四个隔离级别](#二、 SQL 标准定义的四个隔离级别)

[1. 读未提交 (Read Uncommitted)](#1. 读未提交 (Read Uncommitted))

[2. 读已提交 (Read Committed, RC)](#2. 读已提交 (Read Committed, RC))

[3. 可重复读 (Repeatable Read, RR)](#3. 可重复读 (Repeatable Read, RR))

[4. 串行化 (Serializable)](#4. 串行化 (Serializable))

[三、 总结对比表](#三、 总结对比表)

[四、 核心知识点补充](#四、 核心知识点补充)

[了解 MySQL 是如何通过 MVCC 和锁机制来实现"可重复读"的吗?这是一个非常高频的面试进阶题。](#了解 MySQL 是如何通过 MVCC 和锁机制来实现“可重复读”的吗?这是一个非常高频的面试进阶题。)

第一部分:MVCC(解决普通查询的并发问题)

[1. 隐式字段与 Undo Log](#1. 隐式字段与 Undo Log)

[2. Read View(一致性视图)------ 关键所在!](#2. Read View(一致性视图)—— 关键所在!)

[3. 判定规则](#3. 判定规则)

[第二部分:Next-Key Lock(解决"当前读"的幻读问题)](#第二部分:Next-Key Lock(解决“当前读”的幻读问题))

[1. 锁的分类](#1. 锁的分类)

[2. 如何运作?](#2. 如何运作?)

第三部分:面试官可能会问的"特殊情况"

终极总结(一句话回答面试官)

[详细介绍一下这个read view 实现的底层原理](#详细介绍一下这个read view 实现的底层原理)

[一、 Read View 的核心数据结构](#一、 Read View 的核心数据结构)

[二、 可见性判断算法 (Visibility Algorithm)](#二、 可见性判断算法 (Visibility Algorithm))

[1. 检查是不是我自己改的?](#1. 检查是不是我自己改的?)

[2. 检查是不是"很久以前"就提交的?](#2. 检查是不是“很久以前”就提交的?)

[3. 检查是不是"未来"才发生的?](#3. 检查是不是“未来”才发生的?)

[4. 最复杂的中间情况(落在 min 和 max 之间)](#4. 最复杂的中间情况(落在 min 和 max 之间))

[三、 图解案例演示](#三、 图解案例演示)

[四、 为什么 RC 和 RR 表现不同?](#四、 为什么 RC 和 RR 表现不同?)

总结

RR这个不是不能解决幻读的问题吗?

[一、 理论 vs. 现实](#一、 理论 vs. 现实)

[1. SQL 标准定义(你的理解是对的)](#1. SQL 标准定义(你的理解是对的))

[2. MySQL InnoDB 的"超纲"实现](#2. MySQL InnoDB 的“超纲”实现)

[二、 为什么会有争议?(那个 1% 的例外)](#二、 为什么会有争议?(那个 1% 的例外))

能够复现幻读的特殊场景:

[三、 面试满分回答模板](#三、 面试满分回答模板)

总结

[1. 场景设定](#1. 场景设定)

[2. 对应 Read View 的参数](#2. 对应 Read View 的参数)

[3. 解答你的疑惑](#3. 解答你的疑惑)

[4. 一张图总结](#4. 一张图总结)


事务隔离级别(Transaction Isolation Levels)是数据库设计中非常核心的概念,它属于 ACID 特性中的 I (Isolation, 隔离性)

简单来说,隔离级别定义了当多个用户(事务)同时访问数据库时,他们之间的干扰程度。隔离级别越高,数据越安全(一致性越好),但并发性能通常越低。

为了理解隔离级别,我们首先需要知道如果没有隔离,会发生什么并发问题


一、 三大并发读问题

当多个事务并发执行时,如果不进行隔离,会出现以下三种主要问题:

  1. 脏读 (Dirty Read):

    • 现象: 事务 A 修改了数据但尚未提交,事务 B 就读取到了这个数据。如果事务 A 后来回滚(撤销)了,那么事务 B 读到的就是"脏"数据(根本不存在的数据)。

    • 例子: 老板发工资,把你工资改成 2 万(未提交),你查到了 2 万很高兴。结果老板发现改错了,回滚了事务。你读到的 2 万就是脏读。

  2. 不可重复读 (Non-repeatable Read):

    • 现象: 在同一个事务内,多次读取同一行数据,结果不一样。这是因为在两次读取之间,另一个事务**修改(Update)**或删除并提交了该数据。

    • 重点: 侧重于数据内容被修改。

  3. 幻读 (Phantom Read):

    • 现象: 在同一个事务内,多次执行同一个查询(比如查询"所有工资大于 1 万的人"),发现记录的数量变了(多了或少了一行)。这是因为另一个事务在两次查询之间**插入(Insert)**或删除了符合条件的行。

    • 重点: 侧重于数据条数的变化(像产生了幻觉)。


二、 SQL 标准定义的四个隔离级别

数据库定义了四个级别来解决上述问题。级别从低到高,隔离性越来越强,但性能越来越差。

1. 读未提交 (Read Uncommitted)
  • 含义: 一个事务可以读取另一个未提交事务修改的数据。

  • 解决的问题: 无。

  • 存在的问题: 脏读、不可重复读、幻读。

  • 特点: 极其不安全,极少在实际项目中使用。

2. 读已提交 (Read Committed, RC)
  • 含义: 一个事务只能读取已经提交的数据。

  • 解决的问题: 脏读

  • 存在的问题: 不可重复读、幻读。

  • 特点:

    • 这是大多数数据库(如 Oracle , SQL Server , PostgreSQL )的默认隔离级别

    • 在这个级别下,虽然解决了脏读,但在同一个事务里,由于别人提交了修改,你前后读取的数据可能不一致。

3. 可重复读 (Repeatable Read, RR)
  • 含义: 确保在同一个事务中,多次读取同一条数据的结果是一样的。即便别人修改并提交了数据,我也看不到(直到我自己的事务结束)。

  • 解决的问题: 脏读、不可重复读

  • 存在的问题: 理论上标准 SQL 中存在幻读

  • 特点:

    • 这是 MySQL (InnoDB 引擎)默认隔离级别

    • 注意: 虽然标准 SQL 定义 RR 级别无法解决幻读,但 MySQL 的 InnoDB 引擎通过 MVCC(多版本并发控制)Next-Key Lock(间隙锁) 技术,在 RR 级别下已经很大程度上避免了幻读

4. 串行化 (Serializable)
  • 含义: 强制事务串行执行。通过强制加锁,让事务排队,一个接一个地做。

  • 解决的问题: 所有并发问题(脏读、不可重复读、幻读)。

  • 特点: 安全性最高,但并发性能最低,极易造成死锁和超时。除非对数据一致性要求极高且并发量很小,否则很少使用。


三、 总结对比表

隔离级别 脏读 不可重复读 幻读 性能 常用场景
Read Uncommitted ✅ 可能 ✅ 可能 ✅ 可能 最高 极少使用
Read Committed (RC) ❌ 解决 ✅ 可能 ✅ 可能 Oracle/PG 默认
Repeatable Read (RR) ❌ 解决 ❌ 解决 ✅ 可能* MySQL 默认
Serializable ❌ 解决 ❌ 解决 ❌ 解决 最低 极严格的财务场景

(注:✅ 表示会发生该问题,❌ 表示该级别解决了该问题。)

( 关于 MySQL RR 级别的幻读:MySQL 通过间隙锁机制解决了大部分幻读,但在特殊语法下仍可能出现)*


四、 核心知识点补充

  1. MVCC (多版本并发控制): 现代数据库(如 MySQL、PostgreSQL)大多不是单纯靠"锁"来实现 RC 和 RR 级别的,而是通过 MVCC。简单说就是给数据拍"快照",读数据时读的是快照版本,写数据时生成新版本,这样读写不冲突,性能更高。

  2. 如何选择?

    • 如果你使用的是 MySQL,通常保持默认的 Repeatable Read 即可。

    • 如果业务允许不可重复读(例如主要是报表统计,或者是对实时性要求极高但一致性要求稍低的场景),有些互联网公司会将 MySQL 调整为 Read Committed 以获得略高的并发度(且减少间隙锁导致的死锁)。

您想了解 MySQL 是如何通过 MVCC 和锁机制来实现"可重复读"的吗?这是一个非常高频的面试进阶题。


了解 MySQL 是如何通过 MVCC 和锁机制来实现"可重复读"的吗?这是一个非常高频的面试进阶题。

这是一个非常棒的切入点!MySQL 的 InnoDB 引擎在"可重复读"(Repeatable Read, RR)级别下的实现是面试中的"重难点",因为它打破了标准 SQL 的定义:

标准 SQL 认为 RR 级别只能解决"不可重复读",无法解决"幻读"。

但在 MySQL InnoDB 中,RR 级别实际上同时解决了"脏读"、"不可重复读",并很大程度上解决了"幻读"。

它是通过 "两条腿走路" 的策略来实现的:

  1. 对于普通查询(Select): 使用 MVCC(多版本并发控制)

  2. 对于当前读(Select...for update / Update / Delete): 使用 Next-Key Lock(临键锁)

下面为你深度拆解这两个机制。


第一部分:MVCC(解决普通查询的并发问题)

MVCC (Multi-Version Concurrency Control) 的核心思想是:"你读你的快照,我改我的新值,互不干扰。"

在 RR 级别下,MVCC 是如何保证"可重复读"的呢?

1. 隐式字段与 Undo Log

每一行数据在背后都有两个隐藏字段:

  • DB_TRX_ID : 最后一次修改该行数据的事务 ID

  • DB_ROLL_PTR : 指向Undo Log(回滚日志)的指针。

当我们修改数据时,旧数据会被写入 Undo Log,形成一条版本链

2. Read View(一致性视图)------ 关键所在!

Read View 相当于一个"快照窗口",它记录了生成快照那一刻,系统中有哪些事务是活跃的(未提交)。

RC(读已提交)与 RR(可重复读)的区别就在于生成 Read View 的时机:

  • RC 级别: 每次 执行 SELECT 语句时,都会重新生成一个新的 Read View。所以你可以看到别人刚提交的数据。

  • RR 级别: 在同一个事务中,只在第一次 执行 SELECT 时生成 Read View,后续所有的查询都复用这同一个 Read View

3. 判定规则

当你去读数据时,InnoDB 会拿着数据的版本号(trx_id)跟你的 Read View 比对:

  • 如果这个版本是在你开启事务之前 提交的 -> 可见

  • 如果这个版本是在你开启事务之后 才生成的 -> 不可见

  • 如果不可见,顺着 Undo Log 找上一个版本,直到找到可见的版本为止。

总结: 因为 RR 级别复用了第一次查询时的 Read View,不管期间别的事务怎么修改、插入数据,你的 Read View 就像一张旧照片,永远定格在事务开始的那一刻。这就解决了不可重复读 和普通查询下的幻读


第二部分:Next-Key Lock(解决"当前读"的幻读问题)

MVCC 只能解决普通的 SELECT(快照读)。但如果你要执行更新操作,或者加锁查询(如 SELECT ... FOR UPDATE),这被称为当前读

当前读必须读取最新的数据,MVCC 在这里就失效了。如果此时没有锁机制,就会出现幻读(比如我想把所有人的工资更新为 0,结果别人刚插入一条新纪录,导致我更新完后发现还有一条没变成 0)。

MySQL 使用 Next-Key Lock 来解决这个问题。

1. 锁的分类
  • Record Lock (记录锁): 锁住某一行记录(例如:锁住 id=5)。

  • Gap Lock (间隙锁): 锁住两条记录之间的空隙,不包含记录本身(例如:锁住 5 到 10 之间的缝隙,防止别人插入 id=7)。

  • Next-Key Lock (临键锁): Record Lock + Gap Lock。既锁住记录,又锁住前面的间隙。

2. 如何运作?

假设表中有一个索引列,值有 1, 5, 10。

事务 A 执行 SELECT * FROM table WHERE id > 5 FOR UPDATE;

InnoDB 会加上 Next-Key Lock

  1. 锁住记录 10

  2. 锁住 (5, 10] 的区间(防止插入 6, 7, 8, 9)。

  3. 锁住 (10, +∞) 的区间(防止插入 11, 12...)。

结果: 其他事务如果想在这个范围内执行 INSERT 操作,会被直接阻塞(Block),直到事务 A 提交。

总结: 通过锁住"间隙",彻底杜绝了别人偷偷插入新数据的可能性,从而在"当前读"下解决了幻读


第三部分:面试官可能会问的"特殊情况"

虽然 MySQL 在 RR 级别下做得很好,但在极端情况下,幻读仍可能发生(虽然很少见)。

场景:

  1. 事务 A 开启。

  2. 事务 A 执行快照读 SELECT * FROM user WHERE id = 10(结果为空)。

  3. 事务 B 插入一条 id = 10 的数据并提交。

  4. 事务 A 再次执行快照读 SELECT(由于复用 Read View,结果还是空,看起来正常)。

  5. 但是! 如果事务 A 执行 UPDATE user SET name='a' WHERE id = 10

    • UPDATE 是当前读,它能读到事务 B 提交的最新数据。

    • 更新成功后,这条数据的 trx_id 变成了事务 A 自己的 ID。

  6. 事务 A 再次 SELECT

    • 根据 MVCC 规则,看到是自己修改的数据,可见

    • 结果: 事务 A 突然读到了 id = 10 的记录。幻读出现了


终极总结(一句话回答面试官)

MySQL InnoDB 在可重复读(RR)级别下,通过 MVCC(多版本并发控制) 配合 Read View 的复用 解决了普通查询的不可重复读和幻读;同时利用 Next-Key Lock(临键锁) 锁住记录和间隙,解决了当前读(Locking Read)下的幻读问题。


详细介绍一下这个read view 实现的底层原理

这是一个非常硬核且底层的问题。真正理解了 Read View(一致性视图) 的结构和算法,你才算真正看透了 MVCC 的本质。

Read View 本质上是一个数据结构,它在事务执行查询的那一刻被创建,相当于给当前数据库系统的所有事务状态拍了一张"快照"。

InnoDB 内部通过这个 Read View 结构,配合每一行数据的隐藏字段 DB_TRX_ID(事务ID),来判断数据"可不可见"。

下面我从 数据结构可见性算法案例演示 三个层面为你拆解底层原理。


一、 Read View 的核心数据结构

在 InnoDB 的源码中,Read View 主要包含 4 个核心字段。理解这 4 个字段的含义是解题的关键:

  1. m_ids (活跃事务列表)

    • 这是一个列表(List),记录了生成 Read View 这一刻,系统里所有**"活跃中"(未提交)**的事务 ID。

    • 含义: 这些事务还没提交,它们改的数据我不应该看到。

  2. min_trx_id (最小活跃事务ID)

    • 也就是 m_ids 里的最小值。

    • 注意: 在 InnoDB 源码中它叫 up_limit_id(这名字容易搞混,记含义就好)。

    • 含义: 凡是事务 ID 小于这个值的,说明在快照生成前就已经提交了,肯定是可见的。

  3. max_trx_id (预分配事务ID)

    • 这是系统当前**"下一个要分配的事务 ID"**(即当前最大事务 ID + 1)。

    • 注意:不是 m_ids 里的最大值,而是系统全局的最大 ID。在 InnoDB 源码中它叫 low_limit_id

    • 含义: 凡是事务 ID 大于等于这个值的,说明是在快照生成之后才开启的事务,肯定是不可见的(属于未来的事务)。

  4. creator_trx_id (创建者事务ID)

    • 即当前这个事务自己的 ID。

    • 含义: 我自己改的数据,我当然能看到。


二、 可见性判断算法 (Visibility Algorithm)

当事务要去读某一行数据时,会拿到这行数据的 trx_id(即最后修改这行数据的事务 ID),然后拿着这个 ID 去跟 Read View 进行比对。

比对逻辑是一个严密的流程式判断(假设当前行数据的事务 ID 为 trx_id):

1. 检查是不是我自己改的?
  • 判断: trx_id == creator_trx_id ?

  • 结果: 如果是,可见

2. 检查是不是"很久以前"就提交的?
  • 判断: trx_id < min_trx_id ?

  • 结果: 如果小于最小值,说明在生成 Read View 之前,修改这行数据的事务就已经提交了。可见

3. 检查是不是"未来"才发生的?
  • 判断: trx_id >= max_trx_id ?

  • 结果: 如果大于等于最大值(预分配ID),说明这个数据是由一个在 Read View 生成之后才启动的事务修改的。不可见

4. 最复杂的中间情况(落在 minmax 之间)
  • 判断: 如果 min_trx_id <= trx_id < max_trx_id

  • 逻辑: 此时需要去 m_ids (活跃列表) 里查一下:

    • 情况 A: trx_id m_ids 列表中。

      • 说明生成快照时,这个事务还没提交(活跃中)。不可见
    • 情况 B: trx_id 不在 m_ids 列表中。

      • 说明虽然这个 ID 比较大,但它在生成快照的那一刻已经提交了(不是活跃状态)。可见

三、 图解案例演示

为了让你彻底明白,我们来模拟一个场景。

场景假设:

  • 系统当前有两个活跃事务,ID 分别为 100200

  • 你是事务 300creator_trx_id = 300)。

  • 系统下一个还没分配的 ID 是 301

此时,事务 300 执行查询,生成的 Read View 如下:

  • m_ids: [100, 200]

  • min_trx_id: 100

  • max_trx_id: 301

  • creator_trx_id: 300

现在数据库里有 4 行数据,它们的 trx_id 分别如下,请判断可见性:

数据行 该行的 trx_id 你的 Read View 比对过程 结果 原因
Row A 50 50 < 100 (min_trx_id) 可见 早已提交
Row B 300 300 == 300 (creator_trx_id) 可见 我自己改的
Row C 400 400 >= 301 (max_trx_id) 不可见 未来的事务改的
Row D 100 [100, 200] 列表里 不可见 它是活跃事务,还没提交
Row E 150 100 < 150 < 301,且不在列表里 可见 虽然比 100 大,但它不在活跃列表,说明生成视图前已提交

如果数据不可见怎么办?

InnoDB 会通过数据行的 DB_ROLL_PTR 指针,沿着 Undo Log 版本链往回找,找到上一个版本,再次用上面的规则判断,直到找到一个"可见"的版本为止。


四、 为什么 RC 和 RR 表现不同?

这个机制完美解释了为什么 RC(读已提交)和 RR(可重复读)表现不同。它们的算法完全一样 ,区别仅仅在于Read View 生成的时机

  1. Read Committed (RC):

    • 每次 执行 SELECT 语句,都会重新生成一个新的 Read View。

    • 后果: 如果事务 100 在你两次查询之间提交了,第二次生成的 Read View 里的 m_ids 就没有 100 了,所以你就能看到它提交的数据。

  2. Repeatable Read (RR):

    • 事务启动后,第一次 执行 SELECT 语句时生成 Read View。

    • 之后在这个事务内,无论查询多少次,都复用这同一个 Read View。

    • 后果: 即使事务 100 后来提交了,但你的 Read View 是旧的,100 依然在你的"活跃列表"里,所以系统判定它"不可见"。这就实现了"可重复读"。

总结

Read View 的底层原理就是:划定一条时间线(快照)。

通过 min_trx_id 和 max_trx_id 确定由于时间太早或太晚而明确可见/不可见的范围;通过 m_ids 列表排除掉那些"当时正忙着"的事务。


RR这个不是不能解决幻读的问题吗?

这是一个非常精准且关键的疑问!你的理解完全符合 SQL 标准(SQL Standard) 的定义,但在 MySQL InnoDB 的具体实现中,情况发生了"越级"。

为了彻底解开这个结,我们需要区分 "教科书上的标准""MySQL 的工程实现"

这就像考试大纲和学霸的实际成绩:

  • 大纲要求(SQL 标准): 考 80 分就算优秀(RR 级别允许幻读)。

  • 学霸表现(MySQL InnoDB): 虽然只要求 80 分,但它实际考了 99 分(RR 级别解决了大部分幻读)。


一、 理论 vs. 现实

1. SQL 标准定义(你的理解是对的)

在 SQL-92 标准中,隔离级别的定义如下:

  • Repeatable Read (RR): 要求解决"脏读"和"不可重复读"。允许"幻读"发生

  • Serializable (串行化): 只有在这个级别,才必须解决"幻读"。

所以,从纯理论或数据库通用的角度来说,RR 确实不能解决幻读。

2. MySQL InnoDB 的"超纲"实现

MySQL 的 InnoDB 引擎为了提升性能,不想让大家在生产环境中都去用慢吞吞的 Serializable 级别,所以它在 RR 级别下多做了很多工作:

  • 快照读(普通 Select): 用 MVCC 解决幻读。

  • 当前读(Select...for update): 用 Next-Key Lock 解决幻读。

结论: MySQL 在 RR 级别下,打破了 标准 SQL 的限制,实质上解决了幻读问题。


二、 为什么会有争议?(那个 1% 的例外)

既然 MySQL 这么厉害,为什么有些严谨的文章还是说"MySQL 的 RR 并没有彻底解决幻读"?

是因为有一个极其特殊的"边缘案例"(Edge Case),在这个案例下,幻读还是会发生。

能够复现幻读的特殊场景:

这个场景利用了 MVCC 的更新机制。

  1. 准备: 表里没有 id=30 的记录。

  2. 事务 A (RR级别): SELECT * FROM table WHERE id=30;

    • 结果:空(正常)。
  3. 事务 B: INSERT INTO table (id, name) VALUES (30, 'ghost');提交

    • 此时数据库里真实存在了 id=30
  4. 事务 A: SELECT * FROM table WHERE id=30;

    • 结果:空(正常,因为 Read View 复用了,看不到事务 B 的新提交)。

    • 到这里为止,幻读都被完美防住了。

  5. 关键操作 -> 事务 A 执行: UPDATE table SET name='caught' WHERE id=30;

    • 注意: UPDATE当前读 !它不看 Read View,而是看数据库最新状态。它发现有 id=30,于是更新成功!

    • 更新后,这行数据的 trx_id 变成了事务 A 自己的 ID。

  6. 事务 A 再次: SELECT * FROM table WHERE id=30;

    • 结果:读到了!

    • 原因:MVCC 规则说"自己修的数据自己可见"。

这就产生了幻读: 事务 A 一开始以为没数据,中间也没人告诉它有数据,结果它随手 update 一下,突然就"变"出了一条数据。


三、 面试满分回答模板

如果面试官问:"RR 级别能解决幻读吗?" 建议按照以下逻辑回答,层次感极强:

  1. 先说标准(表态你懂基础):

    "按照 SQL-92 标准的定义,Repeatable Read (RR) 级别是不要求解决幻读的,只有 Serializable 级别才能解决。"

  2. 再说 MySQL(表态你懂原理):

    "但是,在 MySQL 的 InnoDB 引擎中,它对 RR 级别进行了优化。通过 MVCC 机制解决了快照读下的幻读,通过 Next-Key Lock(间隙锁)解决了当前读下的幻读。"

  3. 最后补刀(表态你懂深度):

    "所以,MySQL 的 RR 级别很大程度上 避免了幻读。不过,在极个别的特殊场景下(比如事务 A 先快照读,事务 B 插入,事务 A 再 Update 该记录),幻读依然可能发生。因此严谨地说,MySQL 在 RR 级别下解决了绝大部分幻读问题。"


总结

  • 你说"RR 不能解决幻读" -> 符合 SQL 标准理论

  • 说"MySQL RR 能解决幻读" -> 符合 MySQL 工程实际

理解了这个差异,你就掌握了数据库面试中最容易混淆的一个知识点。


这句话确实非常绕,尤其是源码里的名字起得还跟直觉相反。

我用**"银行排队取号"**的例子给你翻译一下,保证秒懂。


1. 场景设定

假设银行一直在发号,号码是连续增加的(1, 2, 3...)。

现在的状态是:

  • 100号:还在柜台办事(没走)。

  • 101号 :动作很快,办完走了(提交了)。

  • 102号:还在柜台办事(没走)。

  • 103号 :动作很快,办完走了(提交了)。

这个时候,你(作为 104号)进来了,你看了一眼大厅的情况,生成了一个 Read View(快照)。

2. 对应 Read View 的参数

这时候,你的 Read View 数据如下:

  • m_ids (活跃列表)[100, 102]

    • 解释: 只有这两个人还在办事,其他的要么办完走了,要么还没来。
  • min_trx_id (最小活跃ID)100

    • 解释: 活着的人里,号码最小的是 100。
  • max_trx_id (也就是系统全局最大ID / low_limit_id)105

    • 注意: 这里是关键!为什么是 105?

    • 因为 100, 101, 102, 103 都已经出现过了。你是 104。

    • 系统下一个要发的号码是 105

3. 解答你的疑惑

疑惑点 A:为什么 max_trx_id 不是 m_ids 里的最大值?

  • 看例子: m_ids 里的最大值是 102

  • 看全局: 但实际上 103 已经来过并且办完走了。如果你认为最大值是 102,那你就会觉得 103 是"未来的人",这就不对了。103 是过去的人,你应该能看到他留下的数据。

  • 结论: m_ids 只是一个**"滞留人员名单"**,它不能代表系统发号发到哪里了。只有 max_trx_id 才知道系统已经发到 104 了,下一个是 105。

疑惑点 B:源码里为什么叫 low_limit_id

  • 这名字特别坑,它是从"不可见范围"的角度命名的。

  • 它的含义是:"不可见范围"的下限 (Low Limit)

  • 人话: 凡是 ID 大于等于这个数的(>= 105),都是将来才出生的人,绝对不可见。它是区分"现在"和"未来"的分界线。

4. 一张图总结

Plaintext

java 复制代码
时间轴: -------------------------------------------->

事务ID:   99(已走)    100(在办)    101(已走)    102(在办)    103(已走)    |    105(还没来)
                      ^                        ^                       |     ^
                      |                        |                       |     |
m_ids(活跃列表):   [ 100,                     102 ]                    |     |
                                                                       |     |
min_trx_id:       100 (活跃列表里的最小值)                               |     |
                                                                       |     |
Read View 生成时刻 (你站在这) -------------------------------------------+     |
                                                                             |
max_trx_id:       105 (下一个要发的号) ---------------------------------------+
  • m_ids 里的最大值是 102。

  • max_trx_id 是 105。

  • 中间那个 103 ,虽然比 102 大,但它不在 m_ids 里,说明它已经提交了,是可以被看见的。

这就是为什么不能用 m_ids 的最大值作为分界线的原因。

相关推荐
lifewange2 小时前
数据库索引里面的游标是什么?
数据库·oracle
PhDTool3 小时前
计算机化系统验证(CSV)的前世今生
数据库·安全·全文检索
banpu3 小时前
Spring相关
数据库·spring·sqlserver
老年DBA3 小时前
Ora2Pg 迁移Oracle至 PostgreSQL 之实战指南
数据库·postgresql·oracle
我是苏苏3 小时前
MSSQL04: SQLserver的用户权限管理
数据库
l1t3 小时前
达梦数据库和Oracle兼容性和性能比较
数据库·sql·oracle·达梦
lkbhua莱克瓦243 小时前
基础-事务
开发语言·数据库·笔记·mysql·事务
luoluoal3 小时前
基于python的自然语言处理技术的话题文本分类的研究(源码+文档)
python·mysql·django·毕业设计·源码
weixin_436525073 小时前
NestJS-TypeORM QueryBuilder 常用 SQL 写法
java·数据库·sql