<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>MySQL on 洛克里亚的五度 博客</title>
    <link>https://lv-blog.pages.dev/tags/mysql/</link>
    <description>Recent content in MySQL on 洛克里亚的五度 博客</description>
    <generator>Hugo</generator>
    <language>zh</language>
    <lastBuildDate>Tue, 28 Jul 2026 13:40:09 +0800</lastBuildDate>
    <atom:link href="https://lv-blog.pages.dev/tags/mysql/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>理解MySQL的MVCC</title>
      <link>https://lv-blog.pages.dev/posts/programming/%E7%90%86%E8%A7%A3mysql%E7%9A%84mvcc%E9%82%A3%E4%B9%88%E7%AE%80%E5%8D%95%E4%B8%BA%E4%BB%80%E4%B9%88%E5%B0%B1%E6%B2%A1%E6%9C%89%E4%BA%BA%E8%AF%B4%E4%BA%BA%E8%AF%9D/</link>
      <pubDate>Tue, 28 Jul 2026 13:40:09 +0800</pubDate>
      <guid>https://lv-blog.pages.dev/posts/programming/%E7%90%86%E8%A7%A3mysql%E7%9A%84mvcc%E9%82%A3%E4%B9%88%E7%AE%80%E5%8D%95%E4%B8%BA%E4%BB%80%E4%B9%88%E5%B0%B1%E6%B2%A1%E6%9C%89%E4%BA%BA%E8%AF%B4%E4%BA%BA%E8%AF%9D/</guid>
      <description>&lt;ul&gt;
&lt;li&gt;我们为什么需要 MVCC ？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;没有MVCC（多版本并发控制 Multi-Version Concurrency Control）的情况下，面对数据库事务中&amp;quot;读某一条数据&amp;quot;的行为，我们只能加锁。这个锁可以是共享锁/排他锁，保证&amp;quot;我读的时候没人过来写&amp;quot;，避免脏读（读到和最终结果不一致的数据）。然而一旦加锁，其他线程就要等，一等并发效率就直线下降。&lt;/p&gt;
&lt;p&gt;MVCC 的思路是：读的时候不加锁，而是通过&amp;quot;版本&amp;quot;来判断一条数据对我来说能不能读。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每一行数据的三个隐藏字段ba&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每一条数据除了业务字段外，还有三个隐藏字段：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DB_TRX_ID   最近一次插入/更新/删除该行的事务id
DB_ROLL_PTR 回滚指针，指向这行数据在 undo log 中的上一个版本
DB_ROW_ID   隐藏主键，仅在表没有定义主键时才会生成
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;undo log 把同一行数据的历史版本串成一条链，DB_ROLL_PTR 就是链上的指针，出问题时可以顺着它一路往回找老版本。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Read View：读之前先拍个快照&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在真正读数据之前，我们会先生成一个快照，叫做 read view。它记录了这几样东西：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;m_ids       生成快照那一刻，所有&amp;quot;活跃&amp;quot;（未提交）的事务id列表
min_trx_id  m_ids 里最小的那个id
max_trx_id  系统里下一个将要分配的事务id（也就是目前已知最大事务id + 1）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意 max_trx_id 不是&amp;quot;当前活跃事务里最大的那个&amp;quot;，而是&amp;quot;还没被任何事务用过的、未来第一个可用的id&amp;quot;——因为事务id是全局递增分配的。&lt;/p&gt;
&lt;p&gt;这里要先说清楚一件容易被忽略的事：&lt;strong&gt;read view 什么时候生成，在不同隔离级别下是不一样的。&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;READ COMMITTED（读已提交）：每次执行 SELECT 语句都重新生成一个 read view
REPEATABLE READ（可重复读，MySQL默认）：只在事务内第一次执行 SELECT 时生成一次，之后整个事务复用同一个 read view
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个区别直接决定了&amp;quot;我这次读，能不能看到别人事务提交的新数据&amp;quot;——RC 每次都能看到最新提交的结果，RR 则始终锁定在事务开始时的那个快照上，这也是可重复读名字的由来。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;拿到 read view 之后，怎么判断某一行能不能读&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;现在回到最初的问题：我们要读一行数据，它的 DB_TRX_ID = 200。这条数据被事务200修改过，我们能不能读到它，要按顺序做以下判断：&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
