<?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>存储引擎 on Kernel Notes</title><link>https://my-blog.j8465383071.workers.dev/tags/%E5%AD%98%E5%82%A8%E5%BC%95%E6%93%8E/</link><description>Recent content in 存储引擎 on Kernel Notes</description><generator>Hugo -- 0.147.7</generator><language>zh-cn</language><lastBuildDate>Mon, 07 Sep 2026 15:30:00 +0800</lastBuildDate><atom:link href="https://my-blog.j8465383071.workers.dev/tags/%E5%AD%98%E5%82%A8%E5%BC%95%E6%93%8E/index.xml" rel="self" type="application/rss+xml"/><item><title>PostgreSQL 1-4 存储模块</title><link>https://my-blog.j8465383071.workers.dev/posts/postgresql/1-4-storage/</link><pubDate>Tue, 21 Jan 2025 13:55:00 +0800</pubDate><guid>https://my-blog.j8465383071.workers.dev/posts/postgresql/1-4-storage/</guid><description>&lt;h1 id="摘要">摘要&lt;/h1>
&lt;blockquote>
&lt;p>本博客以循序渐进的方式，依次简要介绍PostgreSQL存储模块的各个子模块的基础功能，提及内容适合数据库内核初学者。本博客旨在介绍核心原理，以便于理解为首，部分内容可能不会100%准确与具体，针对每个提及的子模块，之后会分别单独写博客详细介绍&lt;/p>&lt;/blockquote>
&lt;h1 id="目录">目录&lt;/h1>
&lt;ol>
&lt;li>&lt;a href="#%E6%91%98%E8%A6%81">摘要&lt;/a>&lt;/li>
&lt;li>&lt;a href="#%E7%9B%AE%E5%BD%95">目录&lt;/a>&lt;/li>
&lt;li>&lt;a href="#1-%E6%A6%82%E8%BF%B0">1 概述&lt;/a>
&lt;ol>
&lt;li>&lt;a href="#1-1-PostgreSQL%E7%9A%84%E4%BD%BF%E7%94%A8%E6%B5%81%E7%A8%8B">1.1 PostgreSQL的使用流程&lt;/a>&lt;/li>
&lt;li>&lt;a href="#1-2-PostgreSQL%E7%9A%84%E5%9F%BA%E7%A1%80%E6%9E%B6%E6%9E%84">1.2 PostgreSQL的基础架构&lt;/a>&lt;/li>
&lt;li>&lt;a href="#1-3-PostgreSQL%E7%9A%84%E5%8A%9F%E8%83%BD%E5%9C%BA%E6%99%AF">1.3 PostgreSQL的功能场景&lt;/a>&lt;/li>
&lt;li>&lt;a href="#1-4-PostgreSQL%E5%AD%98%E5%82%A8%E6%A8%A1%E5%9D%97%E7%9A%84%E4%BB%8B%E7%BB%8D%E6%B5%81%E7%A8%8B">1.4 PostgreSQL存储模块的介绍流程&lt;/a>&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>&lt;a href="#2-%E6%89%A7%E8%A1%8C%E6%A8%A1%E5%9D%97">2 执行模块&lt;/a>&lt;/li>
&lt;li>&lt;a href="#3-%E5%AD%98%E5%82%A8%E6%A8%A1%E5%9D%97">3 存储模块&lt;/a>
&lt;ol>
&lt;li>&lt;a href="#3-1-%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84">3.1 存储结构&lt;/a>
&lt;ol>
&lt;li>&lt;a href="#1-data-directory">(1) data_directory&lt;/a>&lt;/li>
&lt;li>&lt;a href="#2-oid">(2) oid&lt;/a>&lt;/li>
&lt;li>&lt;a href="#3-data-file">(3) data-file&lt;/a>&lt;/li>
&lt;li>&lt;a href="#4-page">(4) page&lt;/a>&lt;/li>
&lt;li>&lt;a href="#5-tuple">(5) tuple&lt;/a>&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>&lt;a href="#3-2-%E5%85%B3%E9%94%AE%E6%9C%BA%E5%88%B6">3.2 关键机制&lt;/a>
&lt;ol>
&lt;li>&lt;a href="#1-mvcc">(1) mvcc&lt;/a>&lt;/li>
&lt;li>&lt;a href="#2-delete">(2) delete&lt;/a>&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>&lt;a href="#3-3-%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B">3.3 执行流程&lt;/a>
&lt;ol>
&lt;li>&lt;a href="#1-input">(1) input&lt;/a>&lt;/li>
&lt;li>&lt;a href="#2-heapam">(2) heapam&lt;/a>&lt;/li>
&lt;li>&lt;a href="#3-trancation">(3) trancation&lt;/a>&lt;/li>
&lt;li>&lt;a href="#4-fsm">(4) fsm&lt;/a>&lt;/li>
&lt;li>&lt;a href="#5-buffer">(5) buffer&lt;/a>&lt;/li>
&lt;li>&lt;a href="#6-smgr">(6) smgr&lt;/a>&lt;/li>
&lt;li>&lt;a href="#7-vfd">(7) vfd&lt;/a>&lt;/li>
&lt;li>&lt;a href="#8-vm">(8) vm&lt;/a>&lt;/li>
&lt;li>&lt;a href="#9-wal">(9) wal&lt;/a>&lt;/li>
&lt;li>&lt;a href="#10-wal-buffer">(10) wal-buffer&lt;/a>&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>&lt;a href="#3-4-%E5%85%B3%E9%94%AE%E8%BF%9B%E7%A8%8B">3.4 关键进程&lt;/a>
&lt;ol>
&lt;li>&lt;a href="#1-bgwriter">(1) bgwriter&lt;/a>&lt;/li>
&lt;li>&lt;a href="#2-walwriter">(2) walwriter&lt;/a>&lt;/li>
&lt;li>&lt;a href="#3-checkpointer">(3) checkpointer&lt;/a>&lt;/li>
&lt;li>&lt;a href="#5-walsender">(5) walsender&lt;/a>&lt;/li>
&lt;li>&lt;a href="#6-autovacuum">(6) autovacuum&lt;/a>&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>&lt;a href="#3-5-%E5%85%B3%E9%94%AE%E6%B5%81%E7%A8%8B">3.5 关键流程&lt;/a>
&lt;ol>
&lt;li>&lt;a href="#1-heap-insert">(1) heap_insert&lt;/a>&lt;/li>
&lt;li>&lt;a href="#2-heap-delete">(2) heap_delete&lt;/a>&lt;/li>
&lt;li>&lt;a href="#3-heap-update">(3) heap_update&lt;/a>&lt;/li>
&lt;li>&lt;a href="#4-heap-scan">(4) heap_scan&lt;/a>&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>&lt;a href="#3-6-%E8%BE%85%E5%8A%A9%E6%9C%BA%E5%88%B6">3.6 辅助机制&lt;/a>
&lt;ol>
&lt;li>&lt;a href="#1-index">(1) index&lt;/a>&lt;/li>
&lt;li>&lt;a href="#2-recovery">(2) recovery&lt;/a>&lt;/li>
&lt;li>&lt;a href="#3-backup">(3) backup&lt;/a>&lt;/li>
&lt;li>&lt;a href="#4-toast">(4) toast&lt;/a>&lt;/li>
&lt;li>&lt;a href="#5-logical">(5) logical&lt;/a>&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ol>
&lt;h1 id="1-概述">1 概述&lt;/h1>
&lt;h2 id="11-postgresql的使用流程">1.1 PostgreSQL的使用流程&lt;/h2>
&lt;p>本博客仅讨论存储模块的关键原理。简而言之，PostgreSQL将数据合理地组织并存储在磁盘上，并向用户提供SQL接口，让其读写磁盘上的指定数据。以下是PostgreSQL简单工作流程：&lt;/p></description></item><item><title>PostgreSQL 3-2 Page</title><link>https://my-blog.j8465383071.workers.dev/posts/postgresql/3-2-page/</link><pubDate>Tue, 21 Jan 2025 13:55:00 +0800</pubDate><guid>https://my-blog.j8465383071.workers.dev/posts/postgresql/3-2-page/</guid><description>&lt;h1 id="1-page的概念">1 Page的概念&lt;/h1>
&lt;h2 id="11-前置概念">1.1 前置概念&lt;/h2>
&lt;p>首先，简单介绍一些基本概念：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>关系 Relation&lt;/strong>：PostgreSQL是一款关系型数据库，在数据库中，1个表对应关系代数中的1个关系，即1个Relation。本博客中，将1个Relation指1个表&lt;/li>
&lt;li>&lt;strong>数据文件 RelFile&lt;/strong>：通过&lt;code>CREATE TABLE t1 (c1 INT, c2 TEXT)&lt;/code>等语法创建1个表时，PostgreSQL会生成1个与表对应的数据文件，表中数据都将被存储至数据文件中&lt;/li>
&lt;li>&lt;strong>数据库页 Page&lt;/strong>：1个数据文件，由1个或多个大小为8192字节（即8k）的数据页组成&lt;/li>
&lt;li>&lt;strong>行 Row&lt;/strong>：在SQL语法&lt;code>INSERT INTO t1 VALUES (1, 'data1')&lt;/code>中，&lt;code>(1, 'data1')&lt;/code>是表中的1行数据&lt;/li>
&lt;li>&lt;strong>元组 Tuple&lt;/strong>：在PostgreSQL中，1个Tuple存储1行数据，数据越长，Tuple越长。1个Page可以存储多个Tuple&lt;/li>
&lt;/ol>
&lt;h2 id="12-page">1.2 Page&lt;/h2>
&lt;p>Page的格式如下：&lt;/p>
&lt;p>&lt;img alt="page" loading="lazy" src="https://my-blog.j8465383071.workers.dev/images/PostgreSQL/3-2-page/page.png">&lt;/p></description></item><item><title>PostgreSQL 3-6 Smgr</title><link>https://my-blog.j8465383071.workers.dev/posts/postgresql/3-3-smgr/</link><pubDate>Tue, 21 Jan 2025 13:55:00 +0800</pubDate><guid>https://my-blog.j8465383071.workers.dev/posts/postgresql/3-3-smgr/</guid><description>&lt;p>接口&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">smgrcreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SMgrRelation&lt;/span> &lt;span class="n">reln&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ForkNumber&lt;/span> &lt;span class="n">forknum&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">isRedo&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 创建文件
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">void&lt;/span> &lt;span class="nf">smgrdounlink&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SMgrRelation&lt;/span> &lt;span class="n">reln&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">isRedo&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SMgrRelation&lt;/span> &lt;span class="nf">smgropen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RelFileNode&lt;/span> &lt;span class="n">rnode&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BackendId&lt;/span> &lt;span class="n">backend&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">smgrclose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SMgrRelation&lt;/span> &lt;span class="n">reln&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">smgrwrite&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SMgrRelation&lt;/span> &lt;span class="n">reln&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ForkNumber&lt;/span> &lt;span class="n">forknum&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BlockNumber&lt;/span> &lt;span class="n">blocknum&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">skipFsync&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">smgrread&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SMgrRelation&lt;/span> &lt;span class="n">reln&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ForkNumber&lt;/span> &lt;span class="n">forknum&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BlockNumber&lt;/span> &lt;span class="n">blocknum&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">smgrextend&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SMgrRelation&lt;/span> &lt;span class="n">reln&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ForkNumber&lt;/span> &lt;span class="n">forknum&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BlockNumber&lt;/span> &lt;span class="n">blocknum&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">skipFsync&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>调用关系&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 索引模块
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nf">btbuildempty&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">smgrwrite&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">metapage&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">_bt_blwritepage&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">smgrwrite&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">spgbuildempty&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">smgrwrite&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">|-&lt;/span> &lt;span class="nf">FlushBuffer&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">|-&lt;/span> &lt;span class="nf">FlushRelationBuffers&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">|-&lt;/span> &lt;span class="nf">LocalBufferAlloc&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">smgrwrite&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// FlushBuffer
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="o">|-&lt;/span> &lt;span class="nf">BufferAlloc&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="c1">// readBuffer时，申请一个buffer，可能调用页面淘汰算法
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="o">|-&lt;/span> &lt;span class="nf">FlushDatabaseBuffers&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">|-&lt;/span> &lt;span class="nf">FlushRelationBuffers&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">|-&lt;/span> &lt;span class="nf">FlushOneBuffer&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">FlushBuffer&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// FlushOneBuffer
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nf">XLogReadBufferForRedoExtended&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">FlushOneBuffer&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// FlushRelationBuffers
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="o">|-&lt;/span> &lt;span class="nf">heap_sync&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">|-&lt;/span> &lt;span class="nf">ATExecSetTableSpace&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="c1">// ALTER TABLE SET TABLESPACE
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nf">FlushRelationBuffers&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// heap_sync when HEAP_INSERT_SKIP_WAL
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="o">|-&lt;/span> &lt;span class="nf">intorel_shutdown&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">|-&lt;/span> &lt;span class="nf">CopyFrom&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">|-&lt;/span> &lt;span class="nf">transientrel_shutdown&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">|-&lt;/span> &lt;span class="nf">ATRewriteTable&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">heap_sync&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// FlushDatabaseBuffers
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nf">dbase_redo&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">FlushDatabaseBuffers&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>write时机：&lt;/p>
&lt;ol>
&lt;li>readBuffer时，如果buffer不在内存，且共享缓冲池已满，调用页面置换算法进行刷盘&lt;/li>
&lt;li>alter table set table space&lt;/li>
&lt;li>copy&lt;/li>
&lt;/ol></description></item><item><title>PostgreSQL 3-3 Buffer</title><link>https://my-blog.j8465383071.workers.dev/posts/postgresql/3-4-buffer/</link><pubDate>Tue, 21 Jan 2025 13:55:00 +0800</pubDate><guid>https://my-blog.j8465383071.workers.dev/posts/postgresql/3-4-buffer/</guid><description>&lt;h1 id="1-buffer基本介绍">1 Buffer基本介绍&lt;/h1>
&lt;h2 id="11-buffer解决的问题">1.1 Buffer解决的问题&lt;/h2>
&lt;p>实际应用场景中，PostgreSQL可能需要1分钟处理数十万次事务，频繁读写Page。Buffer提供以下2个基本功能：&lt;/p>
&lt;ul>
&lt;li>加载Page至内存：PostgreSQL需要读写数时，都需先从磁盘中，将指定的Page加载至内存中，再在内存中操作Page&lt;/li>
&lt;li>缓存常用Page：Page被加载至内存后，还会在内存停留一段时间，降低PostgreSQL直接从磁盘读写Page的频率&lt;/li>
&lt;/ul>
&lt;h2 id="12-buffer的基本功能">1.2 Buffer的基本功能&lt;/h2>
&lt;p>Buffer缓存Page时，需考虑以下几个场景：&lt;/p>
&lt;ul>
&lt;li>插入Page：大部分事务操作表的Page时，先从Buffer中查找Page，如果Page不在Buffer中，再从磁盘读取Page，将其放入Buffer中，再操作Buffer中的Page&lt;/li>
&lt;li>查找Page：访问Page时，通过文件名和Page编号等信息可确定唯一Page&lt;/li>
&lt;li>访问Page：所有事务均可访问Buffer，需考虑多事务并发读写同一Pape的场景&lt;/li>
&lt;li>淘汰Page：Buffer能缓存的Page数量有限，在Page数量过多时，需淘汰一些Page&lt;/li>
&lt;li>落盘Page：Buffer中通常缓存表数据文件的Page，前文提到，事务持久化的标志是WAL日志落盘，因此，Buffer中的Page无需实时落盘，仅需异步落盘即可&lt;/li>
&lt;/ul>
&lt;h1 id="2-buffer的基本原理">2 Buffer的基本原理&lt;/h1>
&lt;h2 id="21-插入page">2.1 插入Page&lt;/h2>
&lt;p>插入Page主要由读Page操作触发，上层函数读取Page的主要流程如下：&lt;/p>
&lt;ol>
&lt;li>上层模块统一调用ReadBuffer()接口，读取指定表的指定Page&lt;/li>
&lt;li>ReadBuffer()中，先从Buffer查找Page，如果找到Page，则直接返回Buffer中的Page&lt;/li>
&lt;li>如果未从Buffer找到Page，现在Buffer中预留存储Page的空间&lt;/li>
&lt;li>之后，调用Smgr模块，从磁盘读取指定Page，并将其放入预留的空间内，再返回Buffer中的Page&lt;/li>
&lt;/ol>
&lt;p>函数调用关系大概如下图所示：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ReadBuffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Relation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BlockNumber&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ReadBufferExtended&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ReadBuffer_common&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">BufferAlloc&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="c1"># 2,3 查找Page，如找到，返回；如未找到，预留存储Page的空间&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">smgrread&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">..&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BlockNumber&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 4 如果未找到Page，从磁盘将Page读入预留的Page空间内，返回&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="22-查找page">2.2 查找Page&lt;/h2>
&lt;p>上层函数读Page时，首先进入查找Page阶段。&lt;/p>
&lt;h2 id="11-buffer池模型">1.1 buffer池模型&lt;/h2>
&lt;p>模型：&lt;/p>
&lt;p>&lt;img alt="1" loading="lazy" src="https://my-blog.j8465383071.workers.dev/images/PostgreSQL/3-4-buffer/1.png">&lt;/p>
&lt;h2 id="12-buffer池接口">1.2 buffer池接口&lt;/h2>
&lt;p>缓冲区接口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* bufmgr.h */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* 1 buffer池管理 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">InitBufferPool&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">InitBufferPoolAccess&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">InitBufferPoolBackend&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* 2 buffer管理 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* 2.1 读buffer */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Buffer&lt;/span> &lt;span class="nf">ReadBuffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Relation&lt;/span> &lt;span class="n">reln&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BlockNumber&lt;/span> &lt;span class="n">blockNum&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Buffer&lt;/span> &lt;span class="nf">ReadBufferExtended&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Relation&lt;/span> &lt;span class="n">reln&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ForkNumber&lt;/span> &lt;span class="n">forkNum&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BlockNumber&lt;/span> &lt;span class="n">blockNum&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ReadBufferMode&lt;/span> &lt;span class="n">mode&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BufferAccessStrategy&lt;/span> &lt;span class="n">strategy&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Buffer&lt;/span> &lt;span class="nf">ReadBufferWithoutRelcache&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RelFileNode&lt;/span> &lt;span class="n">rnode&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ForkNumber&lt;/span> &lt;span class="n">forkNum&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BlockNumber&lt;/span> &lt;span class="n">blockNum&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">ReadBufferMode&lt;/span> &lt;span class="n">mode&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BufferAccessStrategy&lt;/span> &lt;span class="n">strategy&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* 2.2 清理buffer */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">oid&lt;/span> &lt;span class="nf">ReleaseBuffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Buffer&lt;/span> &lt;span class="n">buffer&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">UnlockReleaseBuffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Buffer&lt;/span> &lt;span class="n">buffer&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">MarkBufferDirty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Buffer&lt;/span> &lt;span class="n">buffer&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">IncrBufferRefCount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Buffer&lt;/span> &lt;span class="n">buffer&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Buffer&lt;/span> &lt;span class="nf">ReleaseAndReadBuffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Buffer&lt;/span> &lt;span class="n">buffer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Relation&lt;/span> &lt;span class="n">relation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BlockNumber&lt;/span> &lt;span class="n">blockNum&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* 2.3 刷盘buffer */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">CheckPointBuffers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">flags&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">FlushOneBuffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Buffer&lt;/span> &lt;span class="n">buffer&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">FlushRelationBuffers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Relation&lt;/span> &lt;span class="n">rel&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">FlushDatabaseBuffers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Oid&lt;/span> &lt;span class="n">dbid&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">BufmgrCommit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">bool&lt;/span> &lt;span class="nf">BgBufferSync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* 2.4 删除关联buffer */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">DropRelFileNodeBuffers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RelFileNodeBackend&lt;/span> &lt;span class="n">rnode&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ForkNumber&lt;/span> &lt;span class="n">forkNum&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">BlockNumber&lt;/span> &lt;span class="n">firstDelBlock&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">DropRelFileNodesAllBuffers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RelFileNodeBackend&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">rnodes&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">nnodes&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">DropDatabaseBuffers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Oid&lt;/span> &lt;span class="n">dbid&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* 2.5 锁buffer */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">UnlockBuffers&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">LockBuffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Buffer&lt;/span> &lt;span class="n">buffer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">mode&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">bool&lt;/span> &lt;span class="nf">ConditionalLockBuffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Buffer&lt;/span> &lt;span class="n">buffer&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">LockBufferForCleanup&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Buffer&lt;/span> &lt;span class="n">buffer&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">bool&lt;/span> &lt;span class="nf">ConditionalLockBufferForCleanup&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Buffer&lt;/span> &lt;span class="n">buffer&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">bool&lt;/span> &lt;span class="nf">HoldingBufferPinThatDelaysRecovery&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="13-buffer调用">1.3 buffer调用&lt;/h2>
&lt;p>&lt;strong>插入数据&lt;/strong>&lt;/p></description></item><item><title>PostgreSQL 3-5 FSM</title><link>https://my-blog.j8465383071.workers.dev/posts/postgresql/3-7-fsm/</link><pubDate>Tue, 21 Jan 2025 13:55:00 +0800</pubDate><guid>https://my-blog.j8465383071.workers.dev/posts/postgresql/3-7-fsm/</guid><description>&lt;h1 id="1-概述">1 概述&lt;/h1>
&lt;h2 id="11-思想">1.1 思想&lt;/h2>
&lt;p>&lt;strong>fsm结构&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>用数组表示完全二叉树&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl"> &lt;span class="p">|&lt;/span> &lt;span class="m">8&lt;/span> &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">|&lt;/span> &lt;span class="m">4&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="m">8&lt;/span> &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">|&lt;/span> &lt;span class="m">4&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="m">2&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="m">1&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="m">8&lt;/span> &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;h2 id="12-fsm涉及">1.2 FSM涉及&lt;/h2>
&lt;p>&lt;strong>物理页号&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl"> +-------+-------+-------+-------+-------+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">FSMFile &lt;span class="p">|&lt;/span> 8k &lt;span class="p">|&lt;/span> 8k &lt;span class="p">|&lt;/span> 8k &lt;span class="p">|&lt;/span> 8k &lt;span class="p">|&lt;/span> ... &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> +-------+-------+-------+-------+-------+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">FSMBlock &lt;span class="p">|&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="m">1&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="m">2&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="m">3&lt;/span> &lt;span class="p">|&lt;/span> ... &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> +-------+-------+-------+-------+-------+
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>逻辑页号&lt;/strong>&lt;br>
第2层：1个FSMBlock&lt;br>
第1层：4069个FSMBlock&lt;br>
第0层：4069 * 4069个FSMBlock，每个FSM可映射4069个DataBlock&lt;br>
FSMAddress: [层次，页号]，页号即FSMBlock号&lt;br>
每个表最大容量是32T，只需3层也页面即可管理整个表的空闲空间&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># [FSMAddress.level, FSMAddress.logpageno, FSMBlockNum（1个page即1个FSMBlock）]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>2,0,0&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> +---------------------------+---------------------------+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">|&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>1,0,1&lt;span class="o">]&lt;/span> &lt;span class="o">[&lt;/span>1,1,1*4070+1&lt;span class="o">]&lt;/span> ... &lt;span class="o">[&lt;/span>1,4069,4069*4070+1&lt;span class="o">]&lt;/span> 本层共4069个FSMBlock
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> +----------+------------+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">|&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>2,0,1+1&lt;span class="o">]&lt;/span> &lt;span class="o">[&lt;/span>2,1,1+2&lt;span class="o">]&lt;/span> ... &lt;span class="o">[&lt;/span>2,2,1+4069&lt;span class="o">]&lt;/span> ...... 本层共4069*4069个FSMBlock
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">|&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">（映射DataBlock &lt;span class="m">0&lt;/span> ~ 4065）
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>整体架构&lt;/strong>&lt;br>
3层FSMBlock&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>8&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>&lt;span class="m">8&lt;/span> 0&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>&lt;span class="m">8&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="m">0&lt;/span> 0&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>...&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>&lt;span class="m">8&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="m">0&lt;/span> ...&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>8&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>&lt;span class="m">8&lt;/span> 0&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>&lt;span class="m">8&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="m">0&lt;/span> 0&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>...&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">[&lt;/span>&lt;span class="m">8&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="m">0&lt;/span> ...&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>8&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="m">8&lt;/span> 0&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="m">8&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="m">0&lt;/span> 0&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>...&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>&lt;span class="m">8&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="m">0&lt;/span> &lt;span class="m">0&lt;/span> ...&lt;span class="o">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>测试&lt;/p></description></item><item><title>PostgreSQL 3-1 Heapam</title><link>https://my-blog.j8465383071.workers.dev/posts/postgresql/3-10-heapam/</link><pubDate>Tue, 21 Jan 2025 13:55:00 +0800</pubDate><guid>https://my-blog.j8465383071.workers.dev/posts/postgresql/3-10-heapam/</guid><description>&lt;h1 id="1-heamp的简介">1 heamp的简介&lt;/h1>
&lt;p>前置知识：Relation的基本概念，Tuple的基本概念&lt;/p>
&lt;h2 id="11-heapam接口的功能">1.1 heapam接口的功能&lt;/h2>
&lt;p>heapam是存储模块对上层提供的数据读写接口，这些接口通常是操作1个relation中的1个或多个tuple。&lt;br>
本文介绍以下4个最常用的接口，即如何向1个relation中写入、删除、更新、读取1个Tuple，接口定义如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* 向relatiion中写入1条tuple */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">heap_insert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Relation&lt;/span> &lt;span class="n">relation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">HeapTuple&lt;/span> &lt;span class="n">tup&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">...)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* 从relation中删除1条tuple */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">heap_delete&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Relation&lt;/span> &lt;span class="n">relation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ItemPointer&lt;/span> &lt;span class="n">tid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">...)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* 在relation中，将旧tuple标记删除，并写入1个新tuple */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">heap_update&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Relation&lt;/span> &lt;span class="n">relation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ItemPointer&lt;/span> &lt;span class="n">otid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">HeapTuple&lt;/span> &lt;span class="n">newtup&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">...)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/* 从relation中读取数据，每次读取1条数据 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">HeapScanDesc&lt;/span> &lt;span class="nf">heap_beginscan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Relation&lt;/span> &lt;span class="n">relation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">...)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">HeapTuple&lt;/span> &lt;span class="nf">heap_getnext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HeapScanDesc&lt;/span> &lt;span class="n">scan&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="12-调用heapam接口">1.2 调用heapam接口&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="cm">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * 此处，以4个常见的SQL为例，介绍数据库如何调用heapam接口
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * 1. INSERT INTO t1 VALUES (1, &amp;#39;data1&amp;#39;);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * 2. SELECT * FROM t1;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * 3. DELETE FROM t1 WHERE c1 = 1;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * 4. UPDATE t1 SET c2 = &amp;#39;data2&amp;#39; WHERE c1 = 1;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">exec_simple_query&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;SQL语句&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="cm">/* 该函数是所有SQL语句的统一处理入口 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">PortalRun&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">PortalRunMulti&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ProcessQuery&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ExecutorRun&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">standard_ExecutorRun&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ExecutePlan&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ExecProcNode&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="cm">/* 从这里开始分叉，不同类型的SQL语句，由不同函数处理 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ExecModifyTable&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ExecInsert&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="cm">/* 处理：INSERT INTO t1 VALUES (1, &amp;#39;data1&amp;#39;) */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">heap_insert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">relation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">tuple&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">..)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="13-heapam接口的使用场景">1.3 heapam接口的使用场景&lt;/h2>
&lt;p>实际应用场景中，数据库的存储模块需满足以下关键需求：&lt;/p></description></item><item><title>PostgreSQL 3-8 Index</title><link>https://my-blog.j8465383071.workers.dev/posts/postgresql/3-11-index/</link><pubDate>Tue, 21 Jan 2025 13:55:00 +0800</pubDate><guid>https://my-blog.j8465383071.workers.dev/posts/postgresql/3-11-index/</guid><description>&lt;ol>
&lt;li>&lt;a href="#1-%E8%83%8C%E6%99%AF">1 背景&lt;/a>&lt;/li>
&lt;li>&lt;a href="#1-1-%E6%95%B0%E6%8D%AE%E6%A3%80%E7%B4%A2">1.1 数据检索&lt;/a>&lt;/li>
&lt;li>&lt;a href="#1-%E7%B4%A2%E5%BC%95%E6%A6%82%E8%BF%B0">1 索引概述&lt;/a>
&lt;ol>
&lt;li>&lt;a href="#1-1-%E6%A6%82%E8%BF%B0">1.1 概述&lt;/a>&lt;/li>
&lt;li>&lt;a href="#1-2-%E7%B4%A2%E5%BC%95%E5%87%BD%E6%95%B0">1.2 索引函数&lt;/a>
&lt;ol>
&lt;li>&lt;a href="#1-2-1-%E8%B0%83%E7%94%A8%E7%B4%A2%E5%BC%95%E5%87%BD%E6%95%B0">1.2.1 调用索引函数&lt;/a>&lt;/li>
&lt;li>&lt;a href="#1-2-2-%E7%B4%A2%E5%BC%95%E5%87%BD%E6%95%B0">1.2.2 索引函数&lt;/a>&lt;/li>
&lt;/ol>
&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>&lt;a href="#2-%E7%B4%A2%E5%BC%95%E5%AE%9E%E7%8E%B0">2 索引实现&lt;/a>
&lt;ol>
&lt;li>&lt;a href="#2-1-%E5%88%9B%E5%BB%BA%E7%B4%A2%E5%BC%95-ambuild">2.1 创建索引 ambuild&lt;/a>
&lt;ol>
&lt;li>&lt;a href="#%E4%B8%80%E3%80%81build%E6%95%B4%E4%BD%93%E6%B5%81%E7%A8%8B">一、build整体流程&lt;/a>&lt;/li>
&lt;li>&lt;a href="#%E4%BA%8C%E3%80%81build%E6%8F%92%E5%85%A5tuple">二、build插入tuple&lt;/a>&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>&lt;a href="#2-2-%E7%B4%A2%E5%BC%95%E5%86%99%E5%85%A5%E6%95%B0%E6%8D%AE-aminsert">2.2 索引写入数据 aminsert&lt;/a>&lt;/li>
&lt;/ol>
&lt;/li>
&lt;li>&lt;a href="#3-operate">3 operate&lt;/a>&lt;/li>
&lt;/ol>
&lt;h1 id="1-背景">1 背景&lt;/h1>
&lt;h1 id="11-数据检索">1.1 数据检索&lt;/h1>
&lt;p>通常，应用会使用数据库存储大量数据，比如，单个表存储上百万、甚至上百亿条数据。此时，对表中数据进行某些查询时，将变得非常困难，比如等值查询、范围查询、模糊查询等。&lt;/p>
&lt;h1 id="1-索引概述">1 索引概述&lt;/h1>
&lt;h2 id="11-概述">1.1 概述&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>索引方式：postgresql共有5种索引&lt;/p>
&lt;ul>
&lt;li>
&lt;p>唯一索引：不能出现重复的值&lt;/p>
&lt;/li>
&lt;li>
&lt;p>主键索引：主键自动创建唯一索引&lt;/p>
&lt;/li>
&lt;li>
&lt;p>多属性索引：多个列（最多32列）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>部分索引：WHERE过滤索引&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 示例
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INDEX&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">..&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>表达式索引：和部分索引没太大区别，都是额外调用1个函数&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 示例
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INDEX&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">..&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>索引分类：postgresql有多种索引，本文见介绍以下4种&lt;/p>
&lt;ul>
&lt;li>btree&lt;/li>
&lt;li>hash&lt;/li>
&lt;li>gist&lt;/li>
&lt;li>gin&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>索引系统表&lt;/p>
&lt;ul>
&lt;li>
&lt;p>pg_am：存储所有索引的方法&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pg_am&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">amname&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">amhandler&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">amtype&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">-------+-------------+--------
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">btree&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">bthandler&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">hash&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">hashhandler&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">gist&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">gisthandler&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">gin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ginhandler&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">spgist&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">spghandler&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">brin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">brinhandler&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>pg_index：存储所有索引，索引列的下标&lt;/p></description></item><item><title>PostgreSQL 3-9 Vacuum</title><link>https://my-blog.j8465383071.workers.dev/posts/postgresql/3-12-vacuum/</link><pubDate>Tue, 21 Jan 2025 13:55:00 +0800</pubDate><guid>https://my-blog.j8465383071.workers.dev/posts/postgresql/3-12-vacuum/</guid><description>&lt;h1 id="1-概述">1 概述&lt;/h1>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">select&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">reltuples&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">relhasoids&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">oid&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pg_class&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">where&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">relname&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;t1&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- reltuples = 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">select&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pg_relation_filepath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;t1&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- oid = filepath = relfilenode
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">vacuum&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">full&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">t1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">select&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">reltuples&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">relhasoids&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">oid&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pg_class&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">where&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">relname&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;t1&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- oid 不变, reltuples = 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- oid != filepath = relfilenode !=
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">analyze&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">t1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- reltuples = 2，即有效tuple
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>
&lt;p>启动autovacuum进程&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">standard_ProcessUtility&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Node&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">parsetree&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">case&lt;/span> &lt;span class="nl">T_VacuumStmt&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ExecVacuum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">VacuumStmt&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">vacstmt&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">vacuum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RangeVar&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">relation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Oid&lt;/span> &lt;span class="n">relid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">VacuumParams&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">ServerLoop&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">StartAutoVacLauncher&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">AutoVacLauncherMain&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* if fail: 发送信号重启aotuvacuum进程 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">SendPostmasterSignal&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PMSIGNAL_START_AUTOVAC_WORKER&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">sigusr1_handler&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SIGNAL_ARGS&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nf">CheckPostmasterSignal&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PMSIGNAL_START_AUTOVAC_WORKER&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">StartAutovacuumWorker&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Backend&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">bn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">malloc&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bn&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">pid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">StartAutoVacWorker&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">AutoVacWorkerMain&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">argc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">argv&lt;/span>&lt;span class="p">[])()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">BaseInit&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">InitPostgres&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">NULL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">dbid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">InvalidOid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">dbname&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">do_autovacuum&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">autovacuum_do_vac_analyze&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">autovac_table&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">tab&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">vacuum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RangeVar&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">relation&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Oid&lt;/span> &lt;span class="n">relid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">VacuumParams&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">proc_exit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">dlist_push_head&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">BackendList&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">bn&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">elem&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>查找所有待vacuum的表&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">do_autovacuum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="cm">/* table-by-table */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">StartTransactionCommand&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* 查询pg_database系统表 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tuple&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">SearchSysCache1&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DATABASEOID&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* 打开pg_class系统表 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">classRel&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">heap_open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RelationRelationId&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">pg_class_desc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">CreateTupleDescCopy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">RelationGetDescr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">classRel&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* 为toast表创建hash表 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">table_toast_map&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">hash_create&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* 遍历pg_class系统表，记录所有需要vacuum和analyze的普通表和系统表 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">relScan&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">heap_beginscan_catalog&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">classRel&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">tuple&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">heap_getnext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">relScan&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Form_pg_class&lt;/span> &lt;span class="n">classForm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Form_pg_class&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">GETSTRUCT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tuple&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">relid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">HeapTupleGetOid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tuple&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">relopts&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">extract_autovac_opts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tuple&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">pg_class_desc&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tabentry&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">get_pgstat_tabentry_relid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">relid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">classForm&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">relisshared&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* 检查表是否需要被vacuum和analyze */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">relation_needs_vacanalyze&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">relid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">relopts&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">classForm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">tabentry&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">dovacuum&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">doanalyze&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">dovacuum&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nl">doanalyze&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* 普通表加入list中 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">table_oids&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">lappend_oid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">table_oids&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">relid&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* toast表加入hash表中 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nf">OidIsValid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">classForm&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">reltoastrelid&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">hentry&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">hash_search&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">table_toast_map&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">classForm&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">reltoastrelid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">HASH_ENTER&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">hentry&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ar_relid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">relid&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">eap_endscan&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">relScan&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* 第二次遍历pg_class系统表 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">relScan&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">heap_beginscan_catalog&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">classRel&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">tuple&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">heap_getnext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">relScan&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Form_pg_class&lt;/span> &lt;span class="n">classForm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Form_pg_class&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">GETSTRUCT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tuple&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">relid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">HeapTupleGetOid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tuple&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">relopts&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">extract_autovac_opts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tuple&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">pg_class_desc&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bstrategy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">GetAccessStrategy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">BAS_VACUUM&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* 开始遍历所有list中的表 */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">foreach&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cell&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">table_oids&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Oid&lt;/span> &lt;span class="n">relid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">lfirst_oid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cell&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/* 再次检查是否仍需vacuum */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tab&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">table_recheck_autovac&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">relid&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">table_toast_map&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">pg_class_desc&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">tab&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nb">NULL&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">continue&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MyWorkerInfo&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">wi_tableoid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">relid&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">autovac_balance_cost&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">AutoVacuumUpdateDelay&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">autovacuum_do_vac_analyze&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tab&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">bstrategy&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">vac_update_datfrozenxid&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">CommitTransactionCommand&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>对单个表进行vacuum&lt;/p></description></item><item><title>WAL 加密模块设计</title><link>https://my-blog.j8465383071.workers.dev/posts/security/wal-encryption-design/</link><pubDate>Mon, 07 Sep 2026 15:30:00 +0800</pubDate><guid>https://my-blog.j8465383071.workers.dev/posts/security/wal-encryption-design/</guid><description>&lt;p>假设集群里已经存在表级透明加密模块，在此基础上设计一个高性能、高易用性的 WAL 加密模块。&lt;/p>
&lt;p>WAL 加密的难点几乎不在&amp;quot;加密&amp;quot;本身，而在两件事：&lt;strong>同一个 8KB 页会被反复重写&lt;/strong>，以及&lt;strong>恢复路径要在拿不到 catalog 的时候就能解密&lt;/strong>。下面按设计决策的顺序展开。&lt;/p>
&lt;h2 id="1-威胁模型">1 威胁模型&lt;/h2>
&lt;p>先划清边界，因为它直接决定后面几个取舍。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>要防的&lt;/strong>：磁盘 / 备份 / 归档介质被拖走；DBA 之外的运维人员直接读 &lt;code>pg_wal/&lt;/code>；WAL 归档到对象存储。&lt;/li>
&lt;li>&lt;strong>不防的&lt;/strong>：有 root 权限的在线攻击者（内存里必然有明文）、旁路攻击。&lt;/li>
&lt;/ul>
&lt;h3 id="11-为什么不用-dm-crypt-了事">1.1 为什么不用 dm-crypt 了事&lt;/h3>
&lt;p>如果只是防&amp;quot;介质丢失&amp;quot;，块设备加密（LUKS/dm-crypt）性价比高得多。在数据库内核里做 WAL 加密，真正的理由只有三个：&lt;/p>
&lt;ol>
&lt;li>密钥要由数据库自己管，不能落在 OS 层（密评 / 国测要求）；&lt;/li>
&lt;li>归档件要天然带密；&lt;/li>
&lt;li>和已有的表级 TDE 共用一套密钥体系。&lt;/li>
&lt;/ol>
&lt;p>设计要始终对齐这三个理由，否则很容易做成一个又慢、又没多大意义的模块。&lt;/p>
&lt;h3 id="12-fpi-是必须堵的洞">1.2 FPI 是必须堵的洞&lt;/h3>
&lt;p>既然表级 TDE 已经存在，那么 &lt;strong>FPI（full page image）就是个必堵的泄漏点&lt;/strong>。&lt;/p>
&lt;p>如果 TDE 是在 smgr 层做的（缓冲区里是明文、写盘时加密），那么 WAL 里的 FPI 记录的是&lt;strong>明文页&lt;/strong> —— 加密表的数据会原封不动地漏进 WAL。&lt;/p>
&lt;blockquote>
&lt;p>只要集群里存在任何一张加密表，WAL 加密就必须强制开启，不能是两个独立开关。&lt;/p>&lt;/blockquote>
&lt;p>实现上做成一个集群级的 &lt;code>data_encryption = on&lt;/code>，或者至少让 &lt;code>CREATE ENCRYPTION POLICY&lt;/code> 在 WAL 加密未开启时直接报错。这是易用性设计里最重要的一条：&lt;strong>不要把一个&amp;quot;配错了就静默泄密&amp;quot;的组合留给用户&lt;/strong>。&lt;/p></description></item></channel></rss>