<?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>PostgreSQL on Hellokitty&#39;s Blog</title>
    <link>https://yangyang233333.github.io/tags/postgresql/</link>
    <description>Recent content in PostgreSQL on Hellokitty&#39;s Blog</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Fri, 14 Aug 2026 23:50:00 +0800</lastBuildDate>
    <atom:link href="https://yangyang233333.github.io/tags/postgresql/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>数据库 MVCC 原理与实现：版本链、事务快照和可见性判断</title>
      <link>https://yangyang233333.github.io/posts/database-mvcc-principles-and-implementation/</link>
      <pubDate>Fri, 14 Aug 2026 23:50:00 +0800</pubDate>
      <guid>https://yangyang233333.github.io/posts/database-mvcc-principles-and-implementation/</guid>
      <description>&lt;p&gt;MVCC（Multi-Version Concurrency Control，多版本并发控制）是现代数据库实现高并发事务的重要机制。它的核心思想并不复杂：&lt;strong&gt;数据被修改时，不立即丢弃旧值，而是保留多个版本；事务读取数据时，根据自己的快照选择可见版本。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;因此，读事务可以继续访问旧版本，写事务则创建新版本。普通读取通常不需要等待并发写入完成，写入也不必等待读事务释放共享锁。&lt;/p&gt;
&lt;p&gt;本文从 MVCC 要解决的问题出发，依次说明版本存储、事务快照、可见性判断、写冲突控制和旧版本回收，并比较 InnoDB 与 PostgreSQL 的典型实现。&lt;/p&gt;
&lt;h2 id=&#34;一为什么需要-mvcc&#34;&gt;一、为什么需要 MVCC&lt;/h2&gt;
&lt;p&gt;假设数据库中存在一条记录：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;账户 A 的余额 = 100
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;事务 T1 正在读取这条记录，与此同时，事务 T2 希望把余额修改为 &lt;code&gt;80&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果所有读取都依赖共享锁，那么 T2 可能必须等待 T1 结束；如果 T2 已经持有排他锁，T1 又可能必须等待 T2。锁能够保证正确性，但大量读写互相等待会降低系统并发能力，并增加死锁概率。&lt;/p&gt;
&lt;p&gt;MVCC 使用另一种思路处理普通读取：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;新版本：balance = 80，由 T2 创建
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                 ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;旧版本：balance = 100
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果 T1 的事务快照不允许它看到 T2 的修改，T1 就读取旧版本 &lt;code&gt;100&lt;/code&gt;；在 T2 提交后启动的新事务，则可以读取新版本 &lt;code&gt;80&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;于是，同一时刻可以出现：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;旧事务看到 100
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;新事务看到 80
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这不是数据不一致，而是两个事务观察到了数据库在不同逻辑时刻的一致状态。&lt;/p&gt;
&lt;h2 id=&#34;二mvcc-的五个组成部分&#34;&gt;二、MVCC 的五个组成部分&lt;/h2&gt;
&lt;p&gt;一个完整的 MVCC 实现通常包含五个部分：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MVCC = 多版本存储
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     + 事务快照
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     + 可见性判断
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     + 写冲突控制
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     + 旧版本回收
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;只保存历史版本还不够。数据库还必须知道版本由谁创建、事务能够看到哪些版本、两个写事务冲突时如何处理，以及历史版本何时可以安全删除。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
