<一图读懂> : MySQL 快照读的读取版本策略

在这里插入图片描述


核心思想:我们活在“不同版本”的世界里

想象一下,你和你的同事们在同时编辑一个共享的云文档。

  • 你在写第一章。
  • 同事A在修改第五章。
  • 同事B刚开始准备写第十章。

为了不让大家互相干扰(比如你看到同事A写了一半的、不通顺的句子),系统必须给你一个“稳定”的版本看。你开始编辑时文档是什么样,在你编辑的过程中它就应该一直是什么样,不应该被别人的修改所干扰。

数据库也面临同样的问题。这张图解释的,就是数据库(特指像 MySQL InnoDB 这样的数据库)如何实现这种“稳定版本”的机制。这个机制的专业术语叫做 MVCC (Multi-Version Concurrency Control),即“多版本并发控制”


第一部分:数据是怎么存放的?(看图的上方表格)

我们先看图最上面的表格部分。这里有一张表,有 nameage 两个我们关心的字段。但是请注意,在数据库的底层,每一行数据后面还悄悄跟着几个额外的信息:

  1. DB_TRX_ID: 事务ID。可以理解为“这次修改是由谁完成的”。每一次对数据库的操作(比如修改“李四”的年龄)都会被分配一个独一无二的、递增的编号,这就是事务ID。
  2. DB_ROLL_PTR: 回滚指针。可以理解为“指向前一个版本的链接”。它指向这条数据被修改之前的样子。
  3. undo log: 历史版本记录本。当数据被修改时,旧的版本不会被立刻删除,而是被存放到一个叫做 undo log 的地方。DB_ROLL_PTR 指针就指向这里。

我们来看图中的例子:

  • 最开始,李四 的年龄是 28,这次修改的事务ID是 10
  • 后来,有人把 李四 的年龄改成了 38。这次修改的事务ID是 11
  • 此时,数据库里最新的数据是 李四, 38, 事务ID=11。但是,它的 回滚指针 指向了它之前的版本,也就是存放在 undo log 里的 李四, 28, 事务ID=10

这就形成了一个版本链。通过回滚指针,数据库可以一路回溯,找到这条数据所有的历史版本。就像一个文档的“修改历史”功能。

小结 1: 数据库中的每一行数据,都可能存在一个由新到旧的“版本链”,新版本指向旧版本。


第二部分:一致性的“快照”(看图的中间核心部分)

现在,一个新的事务(我们称之为“我的事务”)开始了,它想要读取数据。问题来了:数据库里同时有很多人在修改数据,有些修改已经完成了(我们称之为“已提交”),有些还在进行中(“未提交”)。“我的事务”应该看到哪个版本的数据呢?

为了解决这个问题,在“我的事务”开始的那一刻,数据库会为它生成一个“快照”(Read View)。

这个“快照”就像你按下了暂停键,给当前所有事务的状态拍了一张照片。这张照片记录了:

  • m_ids: 拍照时,有哪些事务正在运行(还没提交)。图中的例子是 [11, 12, 13, 14]
  • up_limit_id: 正在运行的事务中,最小的ID是什么。图中的例子是 11。所有比 11 还小的ID(比如 10),肯定在拍照前就已经提交了,所以它们做的修改是“可见”的。
  • low_limit_id: 拍照后,下一个将要分配的事务ID是什么。图中的例子是 15。所有ID大于等于 15 的事务,都是在拍照后才开始的,它们做的修改对我来说是“不可见”的。

你可以把这个“快照”想象成一个**“照妖镜”**,当我的事务去读取数据时,它会用这个镜子去照一下数据的版本,来判断这个版本我该不该看。

小结 2: 每个事务开始时,都会获得一个“快照”,这个快照定义了哪些事务的修改对我是可见的,哪些是不可见的。


第三部分:判断规则——我到底能看见谁?

现在,我们拿着这个“照妖镜”(快照),去读取 李四 这条数据。数据库会执行以下判断流程:

  1. 首先拿到最新的版本李四, 38, 事务ID=11

  2. 开始用“照妖镜”判断:这个版本的事务ID是 11

    • 规则一:是不是比 up_limit_id (11) 小?
      • 不是,11 不小于 11。所以不能直接确定它是可见的。
    • 规则二:是不是比 low_limit_id (15) 大或等于?
      • 不是,11 小于 15。所以不能直接确定它是不可见的。
    • 规则三(关键!):既然在 up_limit_idlow_limit_id 之间,那就查一下“正在运行事务列表” m_ids ([11, 12, 13, 14])。
      • 11 在这个列表里!这意味着,在我拍照的那一刻,事务 11 还在运行,没有提交。
      • 结论: 对于“我的事务”来说,事务 11 的修改是“不可见的未来”,我不能看。
  3. 怎么办?

    • 既然最新版本不能看,数据库就会顺着这条数据的 回滚指针(DB_ROLL_PTR),找到它的上一个版本
    • 上一个版本是:李四, 28, 事务ID=10
  4. 对上一个版本,重复用“照妖镜”判断:这个版本的事务ID是 10

    • 规则一:是不是比 up_limit_id (11) 小?
      • 是的,10 小于 11
      • 结论: 这说明事务 10 在我拍照之前早就完成了。它的修改对我来说是“确定的历史”,是可见的!
  5. 最终结果:所以,“我的事务”最终读到的数据是 李四 的年龄为 28

通过这个机制,即使在我读取数据期间,有其他事务(比如事务 11)提交了,我也看不到它的修改,保证了我看到的数据在我整个事务期间都是一致的。


总结一下

这张图的核心逻辑可以概括为:

  1. 版本链(Undo Log):修改数据时,旧版本被保留下来,形成一条历史版本链。这是实现“穿越”的基础。
  2. 快照(Read View):事务开始时,拍下一张“哪些事务已完成,哪些在进行”的照片。这是“穿越”的规则。
  3. 可见性判断:读取数据时,从最新版本开始,用“快照”的规则逐个检查,如果不符合可见性规则,就通过版本链回溯到上一个版本再判断,直到找到一个符合规则的可见版本为止。

这个精妙的设计,就是数据库能够在成千上万个用户同时读写时,依然能保证数据正确、互不干扰的秘密武器。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DevKevin

你们的点赞收藏是对我最大的鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值