<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>IvorySQL Blog</title>
        <link>https://ivorySQL.org/zh-cn/blog</link>
        <description>IvorySQL Blog</description>
        <lastBuildDate>Mon, 20 Jul 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-cn</language>
        <item>
            <title><![CDATA[Postgres_fdw 优化实战：GUC 参数控制 Join Pushdown]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/fdw-pushdown</link>
            <guid>https://ivorySQL.org/zh-cn/blog/fdw-pushdown</guid>
            <pubDate>Mon, 20 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[本文整理于 HOW 2026 演讲内容，演讲者：杨向博，PostgreSQL ACE。]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>本文整理于 HOW 2026 演讲内容，演讲者：杨向博，PostgreSQL ACE。</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="一问题现象一条-sql-的本地与远程执行差距">一、问题现象：一条 SQL 的本地与远程执行差距<a href="https://ivorysql.org/zh-cn/blog/fdw-pushdown#%E4%B8%80%E9%97%AE%E9%A2%98%E7%8E%B0%E8%B1%A1%E4%B8%80%E6%9D%A1-sql-%E7%9A%84%E6%9C%AC%E5%9C%B0%E4%B8%8E%E8%BF%9C%E7%A8%8B%E6%89%A7%E8%A1%8C%E5%B7%AE%E8%B7%9D" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>这是一个来自真实生产环境的案例。一条 SQL 在本地 PostgreSQL 实例上执行只需 <strong>17 毫秒</strong>，但通过 Postgres_fdw 访问远程数据库时，耗时长达 <strong>200 秒</strong>。两者相差超过一万倍。</p>
<p>初步排查时，第一反应往往是怀疑网络问题。但通过 <code>EXPLAIN ANALYZE</code> 查看执行计划后，问题的根源逐渐浮出水面。</p>
<p>在远程端实际执行的 SQL 中，原本的 Join 条件（如 <code>A.sid = B.id</code>）并未被正确下推。取而代之的是，本地的 <code>WHERE</code> 过滤条件（如 <code>xt = 1</code>）被错误地当成了 Join 条件下推到了远程端。最终效果是：远程端返回了大量中间结果数据，真正的 Join 关联却在本地完成，性能自然急剧恶化。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="二根因分析类型转换如何破坏下推逻辑">二、根因分析：类型转换如何破坏下推逻辑<a href="https://ivorysql.org/zh-cn/blog/fdw-pushdown#%E4%BA%8C%E6%A0%B9%E5%9B%A0%E5%88%86%E6%9E%90%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2%E5%A6%82%E4%BD%95%E7%A0%B4%E5%9D%8F%E4%B8%8B%E6%8E%A8%E9%80%BB%E8%BE%91" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-postgres_fdw-的-join-下推机制">2.1 Postgres_fdw 的 Join 下推机制<a href="https://ivorysql.org/zh-cn/blog/fdw-pushdown#21-postgres_fdw-%E7%9A%84-join-%E4%B8%8B%E6%8E%A8%E6%9C%BA%E5%88%B6" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>Postgres_fdw 并非无条件地将 Join 操作下推到远程端。优化器会综合评估代价，在四种 Join 路径（Nestloop、Hash Join、Merge Join，以及 Foreign Join）中选择最优方案。即使使用了 Foreign Join，也未必一定下推——仍需要通过代价模型比较才能决定。</p>
<p>具体到下推的条件，核心校验在 <code>is_foreign_expr</code> 函数中完成，需同时满足以下要求：</p>
<ul>
<li class=""><strong>Join 类型支持</strong>：仅限 Inner Join、Left Join 等特定类型</li>
<li class=""><strong>外表安全性标记</strong>：外表必须被标记为 <code>safety</code>（安全）</li>
<li class=""><strong>表达式限制</strong>：内外表的 Join 条件不能包含不可下推的操作符或函数</li>
<li class=""><strong>最终校验</strong>：<code>is_foreign_expr</code> 必须返回 True</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-本次案例的故障链条">2.2 本次案例的故障链条<a href="https://ivorysql.org/zh-cn/blog/fdw-pushdown#22-%E6%9C%AC%E6%AC%A1%E6%A1%88%E4%BE%8B%E7%9A%84%E6%95%85%E9%9A%9C%E9%93%BE%E6%9D%A1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>本案例中的 SQL 存在一个看似无害的写法：<code>A.sid</code> 和 <code>B.id</code> 两个字段都被显式进行了类型转换（转为 <code>text</code>）。这个类型转换节点触发了以下连锁反应：</p>
<ol>
<li class=""><code>is_foreign_expr</code> 函数检测到类型转换节点，将其归入 <strong>"未知或不安全表达式"</strong> 分支（default 分支），返回 <code>False</code></li>
<li class="">原本合法的 Join 条件（<code>A.sid = B.id</code>）被拒绝下推</li>
<li class="">FDW 的优化逻辑中，为了避免子查询等问题，会尝试将其他合法条件合并到 Join 子句中</li>
<li class="">此时唯一合法的条件只剩下本地的 <code>WHERE xt = 1</code>，于是该条件被强行"伪装"成 Join 条件下推到远程端</li>
<li class="">远程端使用 <code>xt = 1</code> 作为 Join 条件执行，返回大量中间行，真正的表关联留在了本地完成</li>
</ol>
<p>这就是性能雪崩的根本原因。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="三解决方案用-guc-参数兜住性能">三、解决方案：用 GUC 参数"兜住"性能<a href="https://ivorysql.org/zh-cn/blog/fdw-pushdown#%E4%B8%89%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%E7%94%A8-guc-%E5%8F%82%E6%95%B0%E5%85%9C%E4%BD%8F%E6%80%A7%E8%83%BD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-为什么需要-guc-方案">3.1 为什么需要 GUC 方案<a href="https://ivorysql.org/zh-cn/blog/fdw-pushdown#31-%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81-guc-%E6%96%B9%E6%A1%88" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>直接修改 SQL 去除类型转换显然是最彻底的解决方式。但在实际场景中，这种写法往往涉及历史遗留系统或第三方封闭软件，SQL 不在应用方的可控范围内。修改 SQL 这条路走不通。</p>
<p>这时候需要从 FDW 内核层面寻找解决方案。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-干预位置与实现思路">3.2 干预位置与实现思路<a href="https://ivorysql.org/zh-cn/blog/fdw-pushdown#32-%E5%B9%B2%E9%A2%84%E4%BD%8D%E7%BD%AE%E4%B8%8E%E5%AE%9E%E7%8E%B0%E6%80%9D%E8%B7%AF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>优化的关键在于：<strong>在优化器生成 Foreign Join Path 的阶段进行干预</strong>。具体来说，在 <code>add_foreign_join_paths</code> 相关的路径生成逻辑中，嵌入一个 GUC 参数开关。</p>
<p>实现逻辑如下：</p>
<ol>
<li class="">新增 GUC 参数（例如 <code>enable_fdw_join_pushdown</code>），默认值为 <code>on</code>（允许 Join 下推）</li>
<li class="">在 <code>is_foreign_expr</code> 校验之前，先检查该参数状态</li>
<li class="">如果参数被设置为 <code>off</code>，则直接阻止 Foreign Join 路径的生成，强制 Join 在本地执行</li>
<li class="">参数可在会话级别动态修改，无需重启数据库</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-优化效果">3.3 优化效果<a href="https://ivorysql.org/zh-cn/blog/fdw-pushdown#33-%E4%BC%98%E5%8C%96%E6%95%88%E6%9E%9C" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>当 GUC 参数关闭 Join 下推后，同样的 SQL 执行时间从 <strong>200 秒</strong> 骤降至 <strong>79 毫秒</strong>。执行计划显示，远程端仅执行了基础表的过滤扫描（<code>SELECT ... WHERE xt = 1</code>），将数据拉回本地后再完成 Join 关联——虽然放弃了远程 Join 的优化机会，但在这个特定场景下，网络传输的数据量远小于错误下推导致的中间结果集，整体性能反而大幅提升。</p>
<p><strong>性能提升约 3000 倍。</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="四经验总结">四、经验总结<a href="https://ivorysql.org/zh-cn/blog/fdw-pushdown#%E5%9B%9B%E7%BB%8F%E9%AA%8C%E6%80%BB%E7%BB%93" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p><strong>第一，排查 FDW 性能问题时，首要步骤是查看远程端实际执行的 SQL。</strong> 仅看本地的执行计划是不够的。使用 <code>EXPLAIN (VERBOSE, ANALYZE)</code> 或者查看远程端的日志，确认下推到远端的 SQL 是否符合预期。</p>
<p><strong>第二，Join 键上的类型转换是 FDW 下推的常见陷阱。</strong> 类型转换节点会被 <code>is_foreign_expr</code> 判定为不安全表达式，导致原本合法的 Join 条件被拒绝下推。在编写跨库 SQL 时，应尽量避免在 Join 键上做显式类型转换。</p>
<p><strong>第三，GUC 参数是快速止损的有效手段。</strong> 当 SQL 层面无法修改时，通过内核参数控制优化器行为，可以在不改变应用代码的前提下解决问题。对于云服务或 DBA 运维团队来说，这种"兜底"能力尤为重要——能在第一时间恢复业务，再从容规划长期方案。</p>
<p>本案例中新增的 GUC 参数虽只是一个简单开关，但背后的思路值得推广：很多 FDW 相关的性能问题，都可以通过类似的"干预点"设计，为用户提供更多的运行时控制能力，而不是将所有的优化决策都固化在代码逻辑中。</p>]]></content:encoded>
            <category>PostgreSQL</category>
            <category>Postgres_fdw</category>
            <category>Pushdown</category>
            <category>Optimization</category>
            <category>HOW2026</category>
        </item>
        <item>
            <title><![CDATA[云环境下 PostgreSQL 的 Cgroup 内存管理实践]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/cgroup-memory</link>
            <guid>https://ivorySQL.org/zh-cn/blog/cgroup-memory</guid>
            <pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[本文整理于 HOW 2026 演讲内容，演讲者：刘智龙，平安科技PG数据库专家。]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>本文整理于 HOW 2026 演讲内容，演讲者：刘智龙，平安科技PG数据库专家。</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="一cgroup内存管理概述">一、Cgroup内存管理概述<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#%E4%B8%80cgroup%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E6%A6%82%E8%BF%B0" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-cgroup的基本原理">1.1 Cgroup的基本原理<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#11-cgroup%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>Cgroup（Control Group）是Linux内核提供的进程组资源管理机制，可对一组进程的CPU时间、物理内存、IO、网络等资源进行配额限制、隔离与使用统计。在云环境中，同一物理主机上通常运行多个PG实例，每个实例通过Cgroup进行资源管理——这是各家云厂商的常见做法，容器技术同样基于Cgroup实现资源约束。</p>
<p>【图1】</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-cpu管理与内存管理的本质差异">1.2 CPU管理与内存管理的本质差异<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#12-cpu%E7%AE%A1%E7%90%86%E4%B8%8E%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E7%9A%84%E6%9C%AC%E8%B4%A8%E5%B7%AE%E5%BC%82" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>Cgroup对CPU和内存的管理逻辑有根本性的不同：</p>
<ul>
<li class="">
<p><strong>CPU控制的核心是时间分配</strong>。CPU通过时间片轮转实现共享，即使某Cgroup的CPU上限达到，任务只是运行变慢，仍可继续执行。</p>
</li>
<li class="">
<p><strong>内存控制的核心是Page计数</strong>。内存必须即时可用，一个任务占用的工作内存无法被其他任务复用（共享内存除外）。当内存达到上限时，不存在"降速运行"的选项，只能进行回收，若回收失败则进程被终止。</p>
</li>
</ul>
<p>关键区别在于：CPU上限打满，任务还能跑，只是慢一些；内存上限打满，任务面临的是回收或死亡。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-云环境下的超卖现象">1.3 云环境下的超卖现象<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#13-%E4%BA%91%E7%8E%AF%E5%A2%83%E4%B8%8B%E7%9A%84%E8%B6%85%E5%8D%96%E7%8E%B0%E8%B1%A1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>在共享云主机上，多个Cgroup实例并发运行。若将所有Cgroup的限额简单相加，可能远超主机的物理内存，即"超卖"。但对于PG来说，超卖并不必然意味着实际内存不足——因为内存中的Cache部分是可以回收的。</p>
<p>实际内存占用可分为三类：<strong>私有内存</strong>（不可回收）、<strong>共享内存</strong>（如PG的Shared Buffers，由多进程共享，不可回收）、<strong>Cache</strong>（文件缓存，可回收）。多个实例的Cache存在大量空闲或可回收空间时，即使限额之和超过物理内存，实际使用量可能并未超限。</p>
<p>但若进行扩容或新增实例，导致OS内存水位线超标，系统会频繁回收Cache。此时回收谁的内存成了"运气问题"，可能波及关键实例，引发性能抖动。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="14-pg在os上的内存层次">1.4 PG在OS上的内存层次<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#14-pg%E5%9C%A8os%E4%B8%8A%E7%9A%84%E5%86%85%E5%AD%98%E5%B1%82%E6%AC%A1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>PG进程的内存占用可分解为三层结构：</p>
<ul>
<li class=""><strong>RSS（常驻内存）</strong> ：分为私有内存和共享内存，通常无法回收</li>
<li class=""><strong>Page Cache</strong>：文件缓存，可回收</li>
<li class=""><strong>Kernel层</strong>：内核自身的内存开销</li>
</ul>
<p>【2】</p>
<p>在Cgroup层面，各实例拥有独立的LRU列表，回收时仅影响本Cgroup，实现了资源隔离。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="二pages的计算与统计口径辨析">二、Pages的计算与统计口径辨析<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#%E4%BA%8Cpages%E7%9A%84%E8%AE%A1%E7%AE%97%E4%B8%8E%E7%BB%9F%E8%AE%A1%E5%8F%A3%E5%BE%84%E8%BE%A8%E6%9E%90" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-关键接口文件">2.1 关键接口文件<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#21-%E5%85%B3%E9%94%AE%E6%8E%A5%E5%8F%A3%E6%96%87%E4%BB%B6" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>Cgroup内存管理涉及的核心文件包括：</p>
<ul>
<li class=""><strong>memory.stat</strong>：包含各类内存指标的详细统计，是观测内存使用的主要入口</li>
<li class=""><strong>memory.usage_in_bytes</strong>：官方不推荐直接使用，准确用量需自行计算</li>
<li class=""><strong>cgroup.procs</strong>：需确保所有PG进程（postmaster、checkpointer、bgwriter、walwriter、backend等）均加入此文件</li>
<li class=""><strong>memory.limit_in_bytes</strong>：定义Cgroup的内存上限</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-memorystat-的核心指标解读">2.2 memory.stat 的核心指标解读<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#22-memorystat-%E7%9A%84%E6%A0%B8%E5%BF%83%E6%8C%87%E6%A0%87%E8%A7%A3%E8%AF%BB" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>以一个实际案例为例：PG实例的<code>shared_buffers=64GB</code>，<code>shared_memory_type=mmap</code>，客户端约800个。<code>memory.stat</code>中的关键指标包括：</p>
<ul>
<li class=""><code>cache</code>：Page Cache的大小</li>
<li class=""><code>rss</code>：匿名页和Swap内存，<strong>注意：这里的RSS与OS进程的RSS含义不同</strong></li>
<li class=""><code>mapped_file</code>：文件共享内存大小，<strong>PG的共享内存在此项中统计</strong></li>
<li class=""><code>pgpgin / pgpgout</code>：RSS+Cache的charge/uncharge页数</li>
<li class=""><code>inactive_anon / active_anon</code>：匿名页和Swap缓存的LRU分布</li>
<li class=""><code>inactive_file / active_file</code>：文件缓存的LRU分布</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-统计口径的核心偏差">2.3 统计口径的核心偏差<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#23-%E7%BB%9F%E8%AE%A1%E5%8F%A3%E5%BE%84%E7%9A%84%E6%A0%B8%E5%BF%83%E5%81%8F%E5%B7%AE" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>通过手动计算可以发现Cgroup v1中的统计混乱问题：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">shared_mem_mapped = inactive_anon + active_anon - rss</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cache - shared_mem_mapped = inactive_file + active_file</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">rss + mapped_file = inactive_anon + active_anon</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">inactive_file + active_file + rss + mapped_file = rss + cache</span><br></span></code></pre></div></div>
<ul>
<li class="">Cgroup v1的RSS统计<strong>不包含file map类型的共享内存</strong></li>
<li class="">PG的共享内存（无论mmap还是sysv方式）均被归类到<code>mapped_file</code>下</li>
<li class="">大页场景下，共享内存甚至不出现在任何统计指标中（包括<code>rss_huge</code>）</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="24-进程rss与cgroup-rss的差异">2.4 进程RSS与Cgroup RSS的差异<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#24-%E8%BF%9B%E7%A8%8Brss%E4%B8%8Ecgroup-rss%E7%9A%84%E5%B7%AE%E5%BC%82" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>查看PG各进程的RSS排序，通常checkpointer和bgwriter的RSS最大（均达60GB级别），这是因为这些进程频繁操作Shared Buffers，实际占用了大量共享内存。postmaster的RSS则小得多，因为它只需开辟共享内存的虚拟地址空间，fork给子进程使用。</p>
<p>查看<code>/proc/[pid]/smaps</code>会发现：checkpointer、bgwriter和postmaster的共享内存虚拟地址完全一致，但RSS不同。fork出的子进程虽然继承了相同的虚拟地址映射，但只有实际访问了对应物理页面的进程，其RSS才会增长。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="25-如何正确模拟cgroup-oom">2.5 如何正确模拟Cgroup OOM<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#25-%E5%A6%82%E4%BD%95%E6%AD%A3%E7%A1%AE%E6%A8%A1%E6%8B%9Fcgroup-oom" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>由于PG采用Double Buffer架构（Shared Buffers + Page Cache），在<code>shared_buffers = 1/4 × cg_mem</code>的典型配置下，Page Cache最多可占3/4的Cgroup内存。正常业务中私有内存占用不会太多，即使Cgroup内存打满，系统也可从Page Cache中回收内存，因此实例未必会OOM。</p>
<p><strong>想要有效触发Cgroup OOM，最直接的方法是创建大量占用私有内存的会话（如大量排序、Hash Join等），而非通过持续写入填满Page Cache。</strong> 后者只会触发Cache回收，不会导致真正的内存耗尽。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="三cgroup-oom的行为与影响">三、Cgroup OOM的行为与影响<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#%E4%B8%89cgroup-oom%E7%9A%84%E8%A1%8C%E4%B8%BA%E4%B8%8E%E5%BD%B1%E5%93%8D" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-oom触发机制">3.1 OOM触发机制<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#31-oom%E8%A7%A6%E5%8F%91%E6%9C%BA%E5%88%B6" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>Cgroup OOM可分为两种情况：</p>
<p><strong>OOM Killer On</strong>：内核根据OOM Score选择得分最高的进程终止（通常是Backend用户进程），发送SIGKILL信号。PG的postmaster检测到子进程异常终止后，若共享内存未被破坏，会自动拉起新进程，业务可能短暂中断但实例整体可恢复。系统日志（dmesg）和PG日志中均有相关记录。</p>
<p><strong>OOM Killer Off</strong>：不触发内核Kill，但PG进程可能因内存分配失败而Hang住（状态变为D，等待事件为<code>mem_cgroup_oom</code>）。关键进程如walwriter若无法获得内存，实例可能直接崩溃。这种情况下，即使OOM Killer关闭，实例仍然可能挂掉——不是因为被杀，而是跑死了。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-关键区别">3.2 关键区别<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#32-%E5%85%B3%E9%94%AE%E5%8C%BA%E5%88%AB" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class="">Cgroup OOM ≠ 系统级VM OOM。系统级VM OOM由<code>vm.overcommit</code>机制独立判断</li>
<li class="">Cgroup v1的<code>memory.oom_control</code>关闭时，OOM Killer不启动，但进程仍可能因内存不足而Hang或崩溃</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="四cgroup-v1的缺陷与v2的改进">四、Cgroup v1的缺陷与v2的改进<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#%E5%9B%9Bcgroup-v1%E7%9A%84%E7%BC%BA%E9%99%B7%E4%B8%8Ev2%E7%9A%84%E6%94%B9%E8%BF%9B" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-v1的统计盲区">4.1 v1的统计盲区<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#41-v1%E7%9A%84%E7%BB%9F%E8%AE%A1%E7%9B%B2%E5%8C%BA" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class=""><strong>缺少Page Table统计</strong>：大量进程可导致Page Table占用数十GB，v1无法观测</li>
<li class=""><strong>缺少Slab统计</strong>：内核Slab内存开销未纳入统计</li>
<li class=""><strong>缺少HugePage统计</strong>：大页内存完全不被计入，形成统计黑洞</li>
<li class=""><strong>无法区分异步/同步回收</strong>：<code>pgscan_kswapd</code>与<code>pgscan_direct</code>在v1中无法按Cgroup粒度区分</li>
<li class=""><strong>共享内存统计混乱</strong>：shmem和file_mapped混在一起</li>
<li class=""><strong>RSS统计口径不一致</strong>：Cgroup RSS与进程RSS含义不同</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-v2的改进">4.2 v2的改进<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#42-v2%E7%9A%84%E6%94%B9%E8%BF%9B" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>Cgroup v2（Linux 4.5+正式发布）在内存管理上提供了显著增强：</p>
<p><strong>管理层面</strong>：</p>
<ul>
<li class="">层级结构更清晰，配置更直观</li>
<li class="">新增<code>memory.min/low/high</code>水位线参数，可更精细地控制回收行为</li>
<li class="">更好地应对突刺负载</li>
<li class="">移除直接关闭OOM Killer的接口，改用更柔和的控制方式</li>
<li class="">新增<code>memory_hugetlb_accounting</code>，终于将大页纳入统计</li>
</ul>
<p><strong>观测层面</strong>：</p>
<ul>
<li class="">新增Slab、Page Table、<code>pgscan_kswapd</code>/<code>pgscan_direct</code>/<code>pgsteal_kswapd</code>等指标</li>
<li class="">新增Socket、Vmalloc、透明大页、Zswap、全零页交换等专项统计</li>
<li class=""><strong>共享内存（shmem）与文件映射（file_mapped）指标分离</strong>，不再混淆</li>
<li class="">大页信息终于可观测</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="五大页huge-pages的管理与挑战">五、大页（Huge Pages）的管理与挑战<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#%E4%BA%94%E5%A4%A7%E9%A1%B5huge-pages%E7%9A%84%E7%AE%A1%E7%90%86%E4%B8%8E%E6%8C%91%E6%88%98" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-大页的优势">5.1 大页的优势<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#51-%E5%A4%A7%E9%A1%B5%E7%9A%84%E4%BC%98%E5%8A%BF" class="hash-link" aria-label="直接链接到标��题" title="直接链接到标题" translate="no">​</a></h3>
<p>大页对PG的稳定性和性能有多方面好处：</p>
<ul>
<li class="">略微提升TPS</li>
<li class="">减少TLB（Translation Lookaside Buffer）刷新压力，降低CPU缓存竞争</li>
<li class=""><strong>显著减少Page Table在主内存中的大小</strong>。这不仅节约内存，还能在内存回收时加速通过物理地址查找虚拟地址的映射过程</li>
<li class="">大页的物理连续性带来更好的内存访问局部性</li>
<li class="">非大页的共享内存理论上可被OS回收，大页不会</li>
</ul>
<p>对于内存碎片和Cgroup内直接内存回收问题，大页有非常好的缓解效果。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-shared-buffers与大页的配置建议">5.2 Shared Buffers与大页的配置建议<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#52-shared-buffers%E4%B8%8E%E5%A4%A7%E9%A1%B5%E7%9A%84%E9%85%8D%E7%BD%AE%E5%BB%BA%E8%AE%AE" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>将<code>shared_buffers</code>设置为Cgroup内存的1/4似乎是行业标准，但实际情况更为复杂。调小<code>shared_buffers</code>可略微增加Page Cache，即增大整体缓存容量；调大<code>shared_buffers</code>则略微减小整体缓存，但提升了Shared Buffers命中率。两种方向各有利弊。</p>
<p>若<code>shared_buffers</code>太小，PG自身可用的工作内存不足，相当于把内存管理责任推给OS，而OS回收Page Cache本身就有性能开销；若<code>shared_buffers</code>太大，不仅挤占Page Cache，还需同步调整bgwriter相关参数（如<code>bgwriter_delay</code>、<code>bgwriter_lru_maxpages</code>），否则刷脏效率跟不上写入负载。</p>
<p>基于实际压测与生产运维经验，一个更靠谱的建议值（至少比简单的1/4公式更可靠）：</p>
<ul>
<li class=""><strong>不开大页</strong>：<code>shared_buffers = min(1/4 × 总内存, 20GB)</code></li>
<li class=""><strong>开大页</strong>：<code>shared_buffers = min(1/4 × 总内存, 60GB)</code></li>
</ul>
<p>读多写少的场景可适当调大，但必须同步调整刷脏参数。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="53-大页带来的管理难题">5.3 大页带来的管理难题<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#53-%E5%A4%A7%E9%A1%B5%E5%B8%A6%E6%9D%A5%E7%9A%84%E7%AE%A1%E7%90%86%E9%9A%BE%E9%A2%98" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p><strong>统计黑洞</strong>：在Cgroup v1中，大页完全不进入任何统计指标。一个实例的Cgroup限额设为20GB，若使用大页，实际可能已占用30GB物理内存，但监控显示一切正常。RDS等监控系统的内存指标在大页场景下可能全面失真。</p>
<p><strong>资源规划困难</strong>：大页需在主机启动时预先分配，无法按需动态扩展。问题在于：</p>
<ul>
<li class="">同一主机可能混合部署PG、MySQL等多种数据库，不同数据库对大页的使用策略不同</li>
<li class="">不知道主机上要部署多少实例、什么规格的实例，大页大小难以预先确定</li>
<li class="">若大页分配过大，后续实例分配不到大页，只能退而使用普通页，违背了启用大页的初衷；若分配过小，则造成大量内存浪费</li>
</ul>
<p>【3】</p>
<p>示例场景：第一个PG实例获得充足大页，第二个实例要上线时大页已耗尽，无法满足需求。若试图为一个大实例分配过多大页，则主机上只能运行这一个实例，资源利用率极低。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="六内存故障模型与排查思路">六、内存故障模型与排查思路<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#%E5%85%AD%E5%86%85%E5%AD%98%E6%95%85%E9%9A%9C%E6%A8%A1%E5%9E%8B%E4%B8%8E%E6%8E%92%E6%9F%A5%E6%80%9D%E8%B7%AF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="61-故障分类">6.1 故障分类<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#61-%E6%95%85%E9%9A%9C%E5%88%86%E7%B1%BB" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>内存问题大致可分为四个层面：</p>
<p><strong>操作系统层面</strong>：内存碎片、Swap换入过高。注意：启用大页可缓解碎片问题，但可能略微加剧Swap换入问题，需权衡。</p>
<p><strong>PG内核层面</strong>：元数据膨胀、版本Bug等。</p>
<p><strong>Cgroup层面</strong>：Cgroup内内存回收引发的性能抖动，通常由Cgroup打满触发。启用大页对此有显著改善效果。</p>
<p><strong>私有内存层面</strong>：个别Backend进程占用过多私有内存（如大结果集排序、Hash Join），导致Cgroup整体内存紧张。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="62-监控要点">6.2 监控要点<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#62-%E7%9B%91%E6%8E%A7%E8%A6%81%E7%82%B9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class=""><strong>系统层</strong>：<code>/proc/buddyinfo</code>查看碎片；<code>/proc/meminfo</code>的<code>si</code>/<code>so</code>（Swap In/Out）判断换入换出压力；<code>/proc/vmstat</code>中的<code>pgscan_kswapd</code>和<code>pgscan_direct</code>区分异步/同步回收</li>
<li class=""><strong>Cgroup层</strong>：<code>memory.stat</code>中的各项指标，注意v1和v2的统计差异</li>
<li class=""><strong>进程层</strong>：<code>ps</code>查看各进程RSS/PSS，计算私有内存；关注进程等待事件（如<code>mem_cgroup_oom</code>）</li>
<li class=""><strong>数据库层</strong>：<code>pg_stat_bgwriter</code>的checkpoint和buffers分配；等待事件中的IPC类事件</li>
</ul>
<p><strong>Buffer命中率方面，Read次数比Hit Rate更值得关注</strong>。Hit Rate很高但Read也很高时，意味着即使命中率高，仍然产生了大量Buffer读取操作（可能因扫描范围太大），这是无效的缓存竞争，仅看命中率会被误导。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="63-故障先导信号">6.3 故障先导信号<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#63-%E6%95%85%E9%9A%9C%E5%85%88%E5%AF%BC%E4%BF%A1%E5%8F%B7" class="hash-link" aria-label="直接链接��到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>常见的前置信号包括：<code>page allocation failure</code>、<code>kswapd</code>长时间高CPU、<code>direct reclaim</code>频繁触发、进程状态频繁切换为D（不可中断睡眠）、<code>malloc</code>相关等待事件增多等。OOM则属于最终结果，往往伴随着明显的进程消失和日志记录。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="总结">总结<a href="https://ivorysql.org/zh-cn/blog/cgroup-memory#%E6%80%BB%E7%BB%93" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>云环境下PG的Cgroup内存管理比单机场景复杂得多。几个核心结论：</p>
<ol>
<li class=""><strong>迁移到Cgroup v2</strong>可解决v1中共享内存统计、大页统计、回收观测等多方面盲区</li>
<li class=""><strong>大页能提升稳定性和性能</strong>，但需配合v2使用，否则形成统计黑洞，资源规划需谨慎</li>
<li class=""><strong>Cgroup OOM的on/off各有风险</strong>，on时Backend进程可能被杀但实例可恢复，off时关键进程可能Hang住导致实例不可用</li>
<li class=""><strong>Shared Buffers不宜超过60GB</strong>（开大页时），超过后刷脏压力和非大页内存管理成本急剧上升</li>
<li class=""><strong>内存监控的核心是掌握真实使用量</strong>，尤其要理解Cgroup RSS与进程RSS的区别、共享内存的统计归属</li>
</ol>]]></content:encoded>
            <category>PostgreSQL</category>
            <category>Cgroup</category>
            <category>Memory</category>
            <category>Cloud</category>
            <category>HOW2026</category>
        </item>
        <item>
            <title><![CDATA[深度拆解 IvorySQL 去 O 核心解决方案]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/oracle-de-o</link>
            <guid>https://ivorySQL.org/zh-cn/blog/oracle-de-o</guid>
            <pubDate>Wed, 15 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[本文整理于 HOW 2026 矫顺田演讲内容。]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>本文整理于 HOW 2026 矫顺田演讲内容。</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-背景与项目概况">1. 背景与项目概况<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#1-%E8%83%8C%E6%99%AF%E4%B8%8E%E9%A1%B9%E7%9B%AE%E6%A6%82%E5%86%B5" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-oracle去o的需求与挑战">1.1 Oracle去O的需求与挑战<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#11-oracle%E5%8E%BBo%E7%9A%84%E9%9C%80%E6%B1%82%E4%B8%8E%E6%8C%91%E6%88%98" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>Oracle长期作为企业核心业务系统的数据库基座，尤其在能源、电信、制造等行业占据主导地位。在“去O”迁移过程中，主要面临以下四类阻力，直接决定迁移项目的成败：</p>
<ul>
<li class=""><strong>语法不兼容</strong>：导致应用代码需大量改动。</li>
<li class=""><strong>语义行为差异</strong>：导致程序虽能运行但结果错误。</li>
<li class=""><strong>资产难以复用</strong>：PL/SQL业务逻辑迁移成本高。</li>
<li class=""><strong>运维复杂度高</strong>：驱动、工具不统一增加协同难度。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-ivorysql项目概况">1.2 IvorySQL项目概况<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#12-ivorysql%E9%A1%B9%E7%9B%AE%E6%A6%82%E5%86%B5" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL于2021年由浪潮软件集团瀚高股份发起，2025年正式捐献给开放原子开源基金会，遵循Apache 2.0开源协议。其核心定位与特点包括：</p>
<ul>
<li class=""><strong>定位</strong>：并非独立DBMS，而是基于PostgreSQL内核深度演进，避免重复造轮子。</li>
<li class=""><strong>使命</strong>：用开源连接世界，提供Oracle替代的开放方案。</li>
<li class=""><strong>目标</strong>：降低迁移门槛，保留PG生态与可持续升级路径。</li>
<li class=""><strong>版本节奏</strong>：紧跟PG内核发布，约40天跟随PG最新版本发版；小版本修复Bug，大版本发布重大兼容特性。</li>
<li class=""><strong>最新版本</strong>：IvorySQL 5.3（基于PostgreSQL 18.3）。</li>
</ul>
<blockquote>
<p>注：目前已发布 <a href="https://github.com/IvorySQL/IvorySQL/releases/tag/IvorySQL_5.4" target="_blank" rel="noopener noreferrer" class="">IvorySQL 5.4</a>（基于 PostgreSQL 18.4）</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-核心特性与社区进展">1.3 核心特性与社区进展<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#13-%E6%A0%B8%E5%BF%83%E7%89%B9%E6%80%A7%E4%B8%8E%E7%A4%BE%E5%8C%BA%E8%BF%9B%E5%B1%95" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL提供全面灵活的Oracle兼容功能，覆盖常见数据类型、字符/时间函数、NLS参数等内置能力。社区自2021年12月发布1.0版本以来，已持续更新至5.4版本。项目提供全平台介质包，支持x86、ARM、MIPS、LoongArch等主流环境，并通过Docker镜像、K8s Operator及浏览器在线体验等方式降低用户评估门槛。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-核心架构与设计理念">2. 核心架构与设计理念<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#2-%E6%A0%B8%E5%BF%83%E6%9E%B6%E6%9E%84%E4%B8%8E%E8%AE%BE%E8%AE%A1%E7%90%86%E5%BF%B5" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-双模式设计">2.1 双模式设计<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#21-%E5%8F%8C%E6%A8%A1%E5%BC%8F%E8%AE%BE%E8%AE%A1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL采用“双模式”架构，初始化时通过<code>initdb -m pg/oracle</code>指定两种模式：</p>
<ul>
<li class=""><strong>PG模式</strong>：保持与原生PostgreSQL完全兼容。</li>
<li class=""><strong>Oracle模式</strong>（默认）：提供Oracle兼容语法及增强功能。</li>
<li class=""><strong>动态切换</strong>：可通过 <code>SET ivorysql.compatible_mode</code> 语句实时动态切换模式，互不牺牲稳定性。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-双端口与双解析器设计">2.2 双端口与双解析器设计<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#22-%E5%8F%8C%E7%AB%AF%E5%8F%A3%E4%B8%8E%E5%8F%8C%E8%A7%A3%E6%9E%90%E5%99%A8%E8%AE%BE%E8%AE%A1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL提供双端口协同解析架构：</p>
<ul>
<li class=""><strong>5432端口</strong>：原生PostgreSQL兼容，保障业务无缝迁移。</li>
<li class=""><strong>1521端口</strong>（Oracle模式默认端口）：企业级Oracle语法支持，开箱即用。</li>
</ul>
<p>在此基础上，采用专利级隔离的双语法解析核心，通过新增Parser模块处理Oracle语法，最大限度降低PostgreSQL与Oracle语法间的相互干扰。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-plisql的实现思路">2.3 PL/iSQL的实现思路<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#23-plisql%E7%9A%84%E5%AE%9E%E7%8E%B0%E6%80%9D%E8%B7%AF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL在系统表<code>pg_language</code>中新增<code>plisql</code>语言，用于承接Oracle风格过程语言。其设计思路是复用并演进PostgreSQL的<code>plpgsql</code>机制，而不是完全重写。Oracle模式初始化时自动注册相关插件与语言能力，通过插件式架构降低对主线内核的侵入度。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="24-五层兼容体系">2.4 五层兼容体系<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#24-%E4%BA%94%E5%B1%82%E5%85%BC%E5%AE%B9%E4%BD%93%E7%B3%BB" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL的兼容体系分为五个层次，前两层解决“能否跑起来”，后三层解决“跑得对不对、好不好”：</p>
<ol>
<li class=""><strong>SQL语法兼容</strong>：覆盖高频DDL/DML，如表创建、修改、查询及MERGE、q转义、LIKE等Oracle语法表达。</li>
<li class=""><strong>对象与存储过程兼容</strong>：支持Package、系统视图及对象访问习惯。</li>
<li class=""><strong>执行语义兼容</strong>：对齐NULL处理、空串转NULL、函数细节与关键边界条件。</li>
<li class=""><strong>过程语言兼容</strong>：适配Oracle执行模型，支持Procedure、Function、Cursor、匿名块等核心能力，与PL/pgSQL共存。</li>
<li class=""><strong>迁移与运维工具</strong>：工具生态的完整闭环。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-关键兼容特性详解">3. 关键兼容特性详解<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#3-%E5%85%B3%E9%94%AE%E5%85%BC%E5%AE%B9%E7%89%B9%E6%80%A7%E8%AF%A6%E8%A7%A3" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-sql语法与对象兼容">3.1 SQL语法与对象兼容<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#31-sql%E8%AF%AD%E6%B3%95%E4%B8%8E%E5%AF%B9%E8%B1%A1%E5%85%BC%E5%AE%B9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class=""><strong>基础DDL/DML</strong>：覆盖建表、修改、查询、写入等典型迁移语句。</li>
<li class=""><strong>Package兼容</strong>：支持Oracle自定义包的功能，包括包和包体的创建、修改和描述，并通过<code>\dk</code>等增强命令提升包管理体验。</li>
<li class=""><strong>View等对象语法</strong>：降低对象层改造成本。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-语义与行为兼容真正的难点">3.2 语义与行为兼容——真正的难点<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#32-%E8%AF%AD%E4%B9%89%E4%B8%8E%E8%A1%8C%E4%B8%BA%E5%85%BC%E5%AE%B9%E7%9C%9F%E6%AD%A3%E7%9A%84%E9%9A%BE%E7%82%B9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>语义行为差异是迁移中最隐蔽的风险。Oracle与PostgreSQL在以下方面存在关键差异：</p>
<table><thead><tr><th>差异点</th><th>Oracle习惯</th><th>IvorySQL处理</th></tr></thead><tbody><tr><td>NULL处理</td><td>空字符串按NULL处理</td><td>兼容模式下对齐Oracle行为</td></tr><tr><td>空串 vs NULL</td><td>空串视为NULL</td><td>通过兼容逻辑减少行为偏移</td></tr><tr><td>函数一致性</td><td>同名函数细节不同</td><td>优先覆盖高频函数并持续验证</td></tr><tr><td>日期/数值</td><td>格式、精度影响业务结果</td><td>重点保障核心口径稳定</td></tr></tbody></table>
<blockquote>
<p>语句能解析，只是迁移的开始；结果是否一致，才决定业务是否敢切换。</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-高级功能兼容">3.3 高级功能兼容<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#33-%E9%AB%98%E7%BA%A7%E5%8A%9F%E8%83%BD%E5%85%BC%E5%AE%B9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class=""><strong>序列与系统视图</strong>：支持CACHE/NOCACHE、SCALE、SESSION、GLOBAL等选项，兼容NEXTVAL和CURRVAL伪列使用习惯，提供ALL_SEQUENCES、DBA_SEQUENCES、USER_SEQUENCES视图。</li>
<li class=""><strong>Invisible Column（不可见列）</strong> ：默认不出现在<code>SELECT *</code>结果中，需显式引用，适合应用升级过程中平滑增加列。</li>
<li class=""><strong>XML函数</strong>：新增11个XML函数，覆盖APPENDCHILDXML、UPDATEXML、EXISTSNODE等高频函数，增强企业对XML数据的处理能力。</li>
<li class=""><strong>系统视图与元数据</strong>：路线规划中包含ALL_TAB_COLUMNS、ALL_CONSTRAINTS、ALL_INDEXES等常用视图，兼容DBA_/USER_体系。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-未来高级特性规划">3.4 未来高级特性规划<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#34-%E6%9C%AA%E6%9D%A5%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E8%A7%84%E5%88%92" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL计划持续补齐以下高级特性：</p>
<ul>
<li class="">自治事务（Autonomous Transaction）</li>
<li class="">ROWNUM伪列</li>
<li class="">Outer Join（+）语法</li>
<li class="">Synonym（同义词）</li>
<li class="">Reverse Key Index、CREATE INDEX ONLINE</li>
<li class="">Pivot/Unpivot、Trigger语法、Bitmap Index、IOT等</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-测试框架与质量保障">3.5 测试框架与质量保障<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#35-%E6%B5%8B%E8%AF%95%E6%A1%86%E6%9E%B6%E4%B8%8E%E8%B4%A8%E9%87%8F%E4%BF%9D%E9%9A%9C" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL建立了双框架协同的测试体系：</p>
<ul>
<li class=""><strong>Oracle test framework</strong>：用Oracle侧测试保障关键兼容点行为对齐。</li>
<li class=""><strong>pg test framework</strong>：复用PG侧成熟验证体系，保持内核稳定性。</li>
<li class="">通过工程化测试减少“能跑但不一致”的风险。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-迁移实践与方法论">4. 迁移实践与方法论<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#4-%E8%BF%81%E7%A7%BB%E5%AE%9E%E8%B7%B5%E4%B8%8E%E6%96%B9%E6%B3%95%E8%AE%BA" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-迁移工具链闭环">4.1 迁移工具链闭环<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#41-%E8%BF%81%E7%A7%BB%E5%B7%A5%E5%85%B7%E9%93%BE%E9%97%AD%E7%8E%AF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL将迁移过程划分为四个阶段：</p>
<ol>
<li class=""><strong>评估阶段</strong>：识别对象差异、风险点与改造范围。</li>
<li class=""><strong>转换阶段</strong>：完成SQL、对象与过程逻辑的自动或半自动转换。</li>
<li class=""><strong>接入阶段</strong>：适配JDBC、ODBC、libpq等驱动，验证PostGIS、FDW、pg_stat_statements等生态组件，适配Kylin、UOS、Anolis等国产环境。</li>
<li class=""><strong>运维阶段</strong>：完成监控、备份、管理与日常交付闭环。</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-全平台交付与云原生能力">4.2 全平台交付与云原生能力<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#42-%E5%85%A8%E5%B9%B3%E5%8F%B0%E4%BA%A4%E4%BB%98%E4%B8%8E%E4%BA%91%E5%8E%9F%E7%94%9F%E8%83%BD%E5%8A%9B" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL提供开箱即用的全平台安装包，降低部署摩擦。在云原生方面，通过Docker镜像、K8s Operator等方式适配现代交付模式。同时提供完善的文档、迁移指南、FAQ及最佳实践，支撑规模化推广。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-案例金融核心系统迁移实践">4.3 案例：金融核心系统迁移实践<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#43-%E6%A1%88%E4%BE%8B%E9%87%91%E8%9E%8D%E6%A0%B8%E5%BF%83%E7%B3%BB%E7%BB%9F%E8%BF%81%E7%A7%BB%E5%AE%9E%E8%B7%B5" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>某金融系统涉及总账等多个核心系统的Oracle替换需求。项目中深度兼容Oracle Package、集合数据类型、系统视图和分区索引。经过严格测试，新平台性能指标与稳定性指标达到预期要求。实践证明，兼容能力能够直接转化为核心业务迁移成功率。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-社区治理与未来路线">5. 社区治理与未来路线<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#5-%E7%A4%BE%E5%8C%BA%E6%B2%BB%E7%90%86%E4%B8%8E%E6%9C%AA%E6%9D%A5%E8%B7%AF%E7%BA%BF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-社区治理与开放协作">5.1 社区治理与开放协作<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#51-%E7%A4%BE%E5%8C%BA%E6%B2%BB%E7%90%86%E4%B8%8E%E5%BC%80%E6%94%BE%E5%8D%8F%E4%BD%9C" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL于2025年捐赠给开放原子开源基金会，治理结构更公开透明。社区欢迎代码、测试、文档、Issue、PR等多类型贡献，GitHub Project与公开协作流程提升了全球开发者协同效率。文档、论坛、FAQ、最佳实践共同构成社区支撑体系。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-生态合作与国际化">5.2 生态合作与国际化<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#52-%E7%94%9F%E6%80%81%E5%90%88%E4%BD%9C%E4%B8%8E%E5%9B%BD%E9%99%85%E5%8C%96" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL与法国Data Bene签署为期两年的MOU（合作备忘录）。Data Bene看重的是PG高兼容接入能力以及Oracle兼容与云原生优势，将在Oracle迁移项目中推广IvorySQL并参与社区能力建设。这说明兼容体系正在从国内项目走向国际生态合作。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="53-ivorysql-60预览与版本规划">5.3 IvorySQL 6.0预览与版本规划<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#53-ivorysql-60%E9%A2%84%E8%A7%88%E4%B8%8E%E7%89%88%E6%9C%AC%E8%A7%84%E5%88%92" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>2026年版本规划如下：</p>
<ul>
<li class="">2026年3月：IvorySQL v5.3（PG 18.3）</li>
<li class="">2026年6月：IvorySQL v5.4（PG 18.4）</li>
<li class="">2026年9月：IvorySQL v5.5（PG 18.5）</li>
<li class="">2026年11月：IvorySQL v6.0（PG 19.0）</li>
<li class="">2026年12月：IvorySQL v6.1（PG 19.1）</li>
</ul>
<p>IvorySQL 6.0重点功能包括：</p>
<ul>
<li class=""><strong>自治事务</strong>：在主事务中独立开启的子事务，可独立提交或回滚。</li>
<li class=""><strong>ROWNUM</strong>：Oracle伪列，用于返回查询结果集中每一行的行号。</li>
<li class=""><strong>Rebuild index</strong>：重建索引以消除碎片、释放空间。</li>
<li class=""><strong>DBMS_OUTPUT</strong>：用于在程序中输出调试信息。</li>
<li class="">组件适配：wal2json、pgbouncer、pg_ai_query、pg_textsearch等。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="54-2026年产品与生态重点">5.4 2026年产品与生态重点<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#54-2026%E5%B9%B4%E4%BA%A7%E5%93%81%E4%B8%8E%E7%94%9F%E6%80%81%E9%87%8D%E7%82%B9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class=""><strong>AI生态打造</strong>：推动数据库能力与AI/Agent场景融合。</li>
<li class=""><strong>新增国产平台适配</strong>：继续扩展认证与适配覆盖面。</li>
<li class=""><strong>云及容器化完整生态</strong>：各版本持续提供云原生适配能力。</li>
<li class=""><strong>文档与解决方案</strong>：建设迁移指南、调优指南、最佳实践与行业方案。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="55-ivorysql创新试验田">5.5 IvorySQL创新试验田<a href="https://ivorysql.org/zh-cn/blog/oracle-de-o#55-ivorysql%E5%88%9B%E6%96%B0%E8%AF%95%E9%AA%8C%E7%94%B0" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL面向真实业务需求的功能孵化平台，汇聚国内客户痛点与前沿实践，将社区暂未接纳的优秀方案快速落地：</p>
<ul>
<li class=""><strong>逻辑复制新增表无主键</strong>：自动添加identity属性，避免报错中断。</li>
<li class=""><strong>创建视图后修改表结构</strong>：允许在视图语义仍有效的前提下安全变更。</li>
<li class=""><strong>全局唯一索引</strong>：在分区表上创建跨所有分区的唯一约束。</li>
</ul>]]></content:encoded>
            <category>IvorySQL</category>
            <category>Oracle</category>
            <category>Migration</category>
            <category>Compatibility</category>
            <category>PL/iSQL</category>
        </item>
        <item>
            <title><![CDATA[没想到！IvorySQL 居然同步了原生 PG18.4 镜像]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/pg18-mirror</link>
            <guid>https://ivorySQL.org/zh-cn/blog/pg18-mirror</guid>
            <pubDate>Mon, 13 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[📖 前言]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/jpeg/26769700/1782804758419-d6166a84-3d31-44d5-92d6-0b7232f229e0.jpeg" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-前言">📖 前言<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#-%E5%89%8D%E8%A8%80" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>前段时间发了一篇 IvorySQL 国内镜像实测文章，不少读者私信问：<strong>仓库里能不能拉原生 PostgreSQL？</strong></p>
<p>今天专门拿 openEuler 真机完整测一遍 PG 18.4-bookworm 镜像，把整套实操、踩坑点一次性说透。</p>
<p>之前用 Docker Hub 拉 PG 镜像，老毛病一堆：⏱️ 高峰几百 KB/s、下到 90% 直接断连，机房无外网代理时干脆完全拉不动。现在 IvorySQL 社区 <code>registry.highgo</code> 仓库同步了官方原生 PG 镜像，不用切换第三方加速源，一套仓库同时跑 IvorySQL、原生 PG，做迁移对比、兼容性测试特别省事。</p>
<p>全文没有套话，全部是真机复制可执行命令，把版本标签坑、磁盘爆满、宿主机无客户端、命令粘贴报错这些运维高频问题全部拆解，新手照着操作也能一次性部署成功。</p>
<blockquote>
<p>🔥 <strong>写在前面：本文所有命令均基于 openEuler 真机实测，截图均为实际操作记录，非模拟演示。</strong></p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="一前置环境说明">一、前置环境说明<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#%E4%B8%80%E5%89%8D%E7%BD%AE%E7%8E%AF%E5%A2%83%E8%AF%B4%E6%98%8E" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>本次实测机器配置：</p>
<table><thead><tr><th>项目</th><th>版本/信息</th></tr></thead><tbody><tr><td>🖥️ <strong>操作系统</strong></td><td>openEuler</td></tr><tr><td>🐳 <strong>Docker</strong></td><td>20.10.24</td></tr><tr><td>📦 <strong>Docker Compose</strong></td><td>v2.20.0</td></tr><tr><td>📍 <strong>镜像仓库</strong></td><td>registry.highgo.com</td></tr><tr><td>🎯 <strong>测试镜像</strong></td><td>registry.highgo.com/postgres/postgres:18.4-bookworm</td></tr></tbody></table>
<div class="language-plain codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-plain codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker --version</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Docker version 20.10.24, build 297e128</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker-compose --version</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Docker Compose version v2.20.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804758085-8af2f9a7-9501-4a67-ab7b-fa793f05c819.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="11--先划重点镜像标签致命大坑">1.1 🚨 先划重点：镜像标签致命大坑<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#11--%E5%85%88%E5%88%92%E9%87%8D%E7%82%B9%E9%95%9C%E5%83%8F%E6%A0%87%E7%AD%BE%E8%87%B4%E5%91%BD%E5%A4%A7%E5%9D%91" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>很多人会直接执行下面这条命令，<strong>百分百报错</strong>：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># ❌ 错误写法，纯数字tag仓库不存在,而且路径也是错的少了postgres/postgres</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">docker pull registry.highgo.com/postgres:18.4</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="111-报错现场">1.1.1 报错现场<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#111-%E6%8A%A5%E9%94%99%E7%8E%B0%E5%9C%BA" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker pull registry.highgo.com/postgres:18.4</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Error response from daemon: unknown: bad request: invalid repository name: postgres</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# </span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804758093-70008948-0862-456d-bc52-d500cace8bee.png" alt="" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="112-原因--正确指令">1.1.2 原因 &amp; 正确指令<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#112-%E5%8E%9F%E5%9B%A0--%E6%AD%A3%E7%A1%AE%E6%8C%87%E4%BB%A4" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<blockquote>
<p>📌 <strong>根因</strong>：仓库内 PG 镜像必须携带系统发行后缀 <code>-bookworm</code> / <code>-trixie</code>，不能只写数字版本。</p>
<p>📌 <strong>正确</strong>：仓库内 PG 镜像的完整路径是 <code>postgres/postgres</code>，不是 <code>postgres</code>；且标签必须带 <code>-bookworm</code> 发行版后缀</p>
</blockquote>
<p>✅ <strong>正确拉取命令</strong>：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker pull registry.highgo.com/postgres/postgres:18.4-bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">18.4-bookworm: Pulling from postgres/postgres</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">dc4bf025fa70: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">07800c4658aa: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">d3b29a572ab1: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">9c32480c36e2: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ab0222f6080d: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">26068876c106: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">204ea99b8184: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">b9b7e4c43bc2: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">d626a1ee2499: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">df093a451bde: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">4551c7621254: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">b7cc028a2062: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">4ea1c3550cc2: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Digest: sha256:d323ae4f611b8c41e2488693785b770d2797c10e859b259e4e7706246debb069</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Status: Downloaded newer image for registry.highgo.com/postgres/postgres:18.4-bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">registry.highgo.com/postgres/postgres:18.4-bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804758453-9aa04963-12c4-4bbf-bf82-957a81720ed3.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="二-极速拉取从20分钟到30秒">二、⚡ 极速拉取：从20分钟到30秒<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#%E4%BA%8C-%E6%9E%81%E9%80%9F%E6%8B%89%E5%8F%96%E4%BB%8E20%E5%88%86%E9%92%9F%E5%88%B030%E7%A7%92" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-执行拉取">2.1 执行拉取<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#21-%E6%89%A7%E8%A1%8C%E6%8B%89%E5%8F%96" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker pull registry.highgo.com/postgres/postgres:18.4-bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">18.4-bookworm: Pulling from postgres/postgres</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">67cf8384fcca: Pull complete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">047252d203fc: Pull complete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">285c515ff12e: Pull complete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">c04bac481cf: Pull complete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">4d2f8983bdeb: Pull complete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">928cbc530e9b: Pull complete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">b7d7c0b1553c: Pull complete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ef11daf91965: Pull complete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">5dab6177213d: Pull complete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">b73f5647695f: Pull complete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">9c1186127d05: Pull complete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">c73bb36f2399: Pull complete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">142af709c9ab: Pull complete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Digest: sha256:1fa4bccc8246ee2ba9592895d804dcf767119e632999ef22d2b3355a93b627c3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Status: Downloaded newer image for registry.highgo.com/postgres/postgres:18.4-bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">registry.highgo.com/postgres/postgres:18.4-bookworm</span><br></span></code></pre></div></div>
<blockquote>
<p>💡 <strong>实测效果</strong>：从敲下回车到下载完毕，<strong>大概只用了30秒左右</strong>。之前在 Docker Hub 上龟速爬行的日子一去不复返，下载速度直接飙到几十 MB/s。这效率，简直是质的飞跃。</p>
</blockquote>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-镜像完整性校验">2.2 镜像完整性校验<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#22-%E9%95%9C%E5%83%8F%E5%AE%8C%E6%95%B4%E6%80%A7%E6%A0%A1%E9%AA%8C" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<p>拉完核对镜像 ID、文件大小，确认无损坏分层：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker images | grep postgres</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">registry.highgo.com/postgres/postgres               18.4-bookworm   dda003c01642   2 weeks ago    440MB</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">registry.highgo.com/postgres/postgres               18.3-bookworm   3c67953c3a90   7 weeks ago    440MB</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804758144-91b59a98-3124-4630-b642-e6101799f1bb.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="三-启动容器连环踩坑实录">三、🚀 启动容器：连环踩坑实录<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#%E4%B8%89-%E5%90%AF%E5%8A%A8%E5%AE%B9%E5%99%A8%E8%BF%9E%E7%8E%AF%E8%B8%A9%E5%9D%91%E5%AE%9E%E5%BD%95" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>镜像拉下来了，接下来就是启动。<strong>这里我要特别提醒一句</strong>：PostgreSQL 的 Docker 镜像和 MySQL / IvorySQL 有点不一样，它默认不会暴露 1521 端口，而且对环境变量的要求比较"倔强"。</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-坑shell-历史扩展--引发的鬼畜报错">3.1 坑①：Shell 历史扩展 <code>!!</code> 引发的"鬼畜"报错<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#31-%E5%9D%91shell-%E5%8E%86%E5%8F%B2%E6%89%A9%E5%B1%95--%E5%BC%95%E5%8F%91%E7%9A%84%E9%AC%BC%E7%95%9C%E6%8A%A5%E9%94%99" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<p>我第一次启动时，习惯性地用了之前 IvorySQL 的命令风格，结果悲剧了：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker run --name pg184 -p 5432:5432 -e POSTGRES_PASSWORD=StrongPass456!! -v pgdata:/var/lib/postgresql/data -d registry.highgo.com/postgres/postgres:18.4-bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Unable to find image 'run:latest' locally</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">docker: Error response from daemon: Get "https://registry-1.docker.io/v2/": dial tcp 202.160.130.66:443: connect: connection timed out.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">See 'docker run --help'.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804759084-dda5437b-306d-42fa-b2e9-d15a94efa591.png" alt="" class="img_ev3q"></p>
<blockquote>
<p>🔍 <strong>根因分析</strong>：你注意看密码：<code>StrongPass456!!</code>。在 Bash 等 Shell 中，<code>!!</code> 是一个特殊指令，意思是"<strong>重复执行上一条命令</strong>"。Shell 在执行前会把 <code>!!</code> 替换成上一条完整命令，结果就"展开"成了一条包含自身的超长命令，自然报错。</p>
</blockquote>
<p>✅ <strong>解决方案</strong>：用单引号或双引号把密码包起来：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker run --name pg184 -p 5432:5432 -e POSTGRES_PASSWORD='StrongPass456!!' -v pgdata:/var/lib/postgresql/data -d registry.highgo.com/postgres/postgres:18.4-bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">5867e8daa44e925e7818598013a1da62ad7dbdd685e6aa0fae68a94d590edbce</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">docker: Error response from daemon: driver failed programming external connectivity on endpoint pg184 (7e11a12a731fb44d5aea01d0363b015deadb223d87df0569a40c46a0698da70e): Bind for 0.0.0.0:5432 failed: port is already allocated.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804759079-e02f9fbc-7de2-4591-bf4e-9059ff37275c.png" alt="" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-坑端口-5432-已被-ivorysql-占用">3.2 坑②：端口 5432 已被 IvorySQL 占用<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#32-%E5%9D%91%E7%AB%AF%E5%8F%A3-5432-%E5%B7%B2%E8%A2%AB-ivorysql-%E5%8D%A0%E7%94%A8" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<p>报错信息：<code>Bind for 0.0.0.0:5432 failed: port is already allocated</code>。</p>
<p>看一眼 <code>docker ps -a</code> 的结果，真相大白：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker ps -a</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">CONTAINER ID   IMAGE                                                      COMMAND                  CREATED          STATUS                   PORTS                                                                                            NAMES</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">5867e8daa44e   registry.highgo.com/postgres/postgres:18.4-bookworm        "docker-entrypoint.s…"   31 seconds ago   Created                                                                                                                   pg184</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">90f5945502a0   registry.highgo.com/ivorysql/ivorysql:5.3-ubi8             "docker-entrypoint.s…"   9 days ago       Up 9 days                0.0.0.0:1521-&gt;1521/tcp, :::1521-&gt;1521/tcp, 0.0.0.0:5432-&gt;5432/tcp, :::5432-&gt;5432/tcp, 5866/tcp   ivorysql</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804758924-2371741c-3883-4963-8030-7d23aedcc69e.png" alt="" class="img_ev3q"></p>
<p>那个叫 <code>ivorysql</code> 的容器已经占了 <code>5432</code>。<strong>一台服务器上，一个端口同一时间只能服务一个程序。</strong></p>
<p>✅ <strong>解决方案</strong>：换个端口映射。把宿主机的 <code>5433</code> 映射到容器的 <code>5432</code>：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker stop pg184</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pg184</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker rm pg184</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pg184</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker run --name pg184 -p 5433:5432 -e POSTGRES_PASSWORD='StrongPass456!!' -v pgdata:/var/lib/postgresql/data -d registry.highgo.com/postgres/postgres:18.4-bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">aa42ab39ec930934befb825efa15e7b646af0cd3b84560c3ff5aba73818af60b</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804759096-61a2161f-be0c-4260-90b5-7b2f95f581d3.png" alt="" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-坑postgresql-18-数据路径-breaking-change">3.3 坑③：PostgreSQL 18+ 数据路径 Breaking Change<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#33-%E5%9D%91postgresql-18-%E6%95%B0%E6%8D%AE%E8%B7%AF%E5%BE%84-breaking-change" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<p>端口解决了，但容器还是没起来：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker ps | grep pg18</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p>查看日志：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker logs pg184</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Error: in 18+, these Docker images are configured to store database data in a</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       format which is compatible with "pg_ctlcluster" (specifically, using</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       major-version-specific directory names).</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       Counter to that, there appears to be PostgreSQL data in:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         /var/lib/postgresql/data (unused mount/volume)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       The suggested container configuration for 18+ is to place a single mount</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       at /var/lib/postgresql which will then place PostgreSQL data in a</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       subdirectory...</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804759442-1e282f7e-37b2-4fb9-adea-e382e8619ac6.png" alt="" class="img_ev3q"></p>
<blockquote>
<p>🔍 <strong>根因分析</strong>：从 PostgreSQL 18 开始，官方 Docker 镜像**不再把数据存在 **<code>/var/lib/postgresql/data</code>，而是放在带版本号的新路径 <code>/var/lib/postgresql/18/docker</code>。你的挂载点 <code>/var/lib/postgresql/data</code> 是旧路径，PG 18 启动时发现路径对不上，直接报错退出。</p>
</blockquote>
<p>✅ <strong>解决方案</strong>：把挂载点从 <code>/var/lib/postgresql/data</code> 改为 <code>/var/lib/postgresql</code>：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker stop pg184</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pg184</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker rm pg184</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pg184</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker run --name pg184 -p 5433:5432 -e POSTGRES_PASSWORD='StrongPass456!!' -v pgdata:/var/lib/postgresql -d registry.highgo.com/postgres/postgres:18.4-bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">0ea9e62cd3d0f19a6a791914f2b946ca417ba7247556a4b8f56b3c851118e2ba</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804759886-f359672f-fe14-4d8e-a22b-b982e0af5855.png" alt="" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="34--终于启动成功">3.4 ✅ 终于启动成功<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#34--%E7%BB%88%E4%BA%8E%E5%90%AF%E5%8A%A8%E6%88%90%E5%8A%9F" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker ps | grep pg18</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">0ea9e62cd3d0   registry.highgo.com/postgres/postgres:18.4-bookworm   "docker-entrypoint.s…"   2 minutes ago   Up 2 minutes   0.0.0.0:5433-&gt;5432/tcp, :::5433-&gt;5432/tcp                                                        pg184</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804759877-ea2fbdbd-2f08-442e-844d-e1da7a2b92d4.png" alt="" class="img_ev3q"></p>
<p>查看日志确认就绪：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker logs pg184 | tail -10</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-27 09:56:43.153 UTC [1] LOG:  starting PostgreSQL 18.4 (Debian 18.4-1.pgdg12+1) on x86_64-pc-linux-gnu</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-27 09:56:43.153 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-27 09:56:43.153 UTC [1] LOG:  listening on IPv6 address "::", port 5432</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-27 09:56:43.155 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-27 09:56:43.161 UTC [73] LOG:  database system was shut down at 2026-06-27 09:56:43 UTC</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-27 09:56:43.170 UTC [1] LOG:  database system is ready to accept connections</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804759558-e65aecf3-28a8-4799-96eb-14a3f0852220.png" alt="" class="img_ev3q"></p>
<p>🎉 <strong>看到</strong> <code>ready to accept connections</code>，<strong>成了！</strong></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="四-生产环境-docker-compose-编排">四、📦 生产环境 Docker Compose 编排<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#%E5%9B%9B-%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83-docker-compose-%E7%BC%96%E6%8E%92" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>单机 run 命令适合临时测试，长期测试、准生产环境统一使用 Compose 管理，配置集中、扩容迁移更便捷。</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-新建编排工作目录">4.1 新建编排工作目录<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#41-%E6%96%B0%E5%BB%BA%E7%BC%96%E6%8E%92%E5%B7%A5%E4%BD%9C%E7%9B%AE%E5%BD%95" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# mkdir -p /data/pg-compose</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# cd /data/pg-compose</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server pg-compose]#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804759852-12def78c-6dea-433a-82ce-46b5e96a400a.png" alt="" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-docker-composeyml-完整配置适配-pg184">4.2 docker-compose.yml 完整配置（适配 PG18.4）<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#42-docker-composeyml-%E5%AE%8C%E6%95%B4%E9%85%8D%E7%BD%AE%E9%80%82%E9%85%8D-pg184" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">root@openeuler</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">server pg</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">compose</span><span class="token punctuation" style="color:#393A34">]</span><span class="token comment" style="color:#999988;font-style:italic"># vim docker-compose.yml</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">version</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'3.8'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">pg18</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> registry.highgo.com/postgres/postgres</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">18.4</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">container_name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> pg18</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">restart</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> always</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"5432:5432"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">environment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">POSTGRES_USER</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> postgres</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">POSTGRES_PASSWORD</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> PGtest@2026</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">POSTGRES_DB</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> testdb</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># ⚠️ PG18+挂载根目录 /var/lib/postgresql，非 /data 子目录</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./pgdata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/var/lib/postgresql</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">networks</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">pg_net</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">networks</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">pg_net</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">driver</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> bridge</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782890471776-ea3a8c12-3a70-4be5-a79a-f472d9a9e76d.png" alt="" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-️-启动报错又一个端口冲突">4.3 ⚠️ 启动报错又一个端口冲突<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#43-%EF%B8%8F-%E5%90%AF%E5%8A%A8%E6%8A%A5%E9%94%99%E5%8F%88%E4%B8%80%E4%B8%AA%E7%AB%AF%E5%8F%A3%E5%86%B2%E7%AA%81" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server pg-compose]# docker-compose up -d</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Error response from daemon: Bind for 0.0.0.0:5432 failed: port is already allocated</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server pg-compose]#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804760378-72c10ceb-377e-4df1-b316-a65e6b0d101a.png" alt="" class="img_ev3q"></p>
<blockquote>
<p>🔍 <strong>根因</strong>：Compose 模板里写的是 <code>"5432:5432"</code>，直接和 IvorySQL 的 <code>5432</code> 端口冲突。调整为 <code>"5435:5432"</code> 即可。</p>
</blockquote>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="44-修改-docker-composeyml-完整配置适配-pg184">4.4 修改 docker-compose.yml 完整配置（适配 PG18.4）<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#44-%E4%BF%AE%E6%94%B9-docker-composeyml-%E5%AE%8C%E6%95%B4%E9%85%8D%E7%BD%AE%E9%80%82%E9%85%8D-pg184" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">root@openeuler</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">server pg</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">compose</span><span class="token punctuation" style="color:#393A34">]</span><span class="token comment" style="color:#999988;font-style:italic"># vim docker-compose.yml</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">version</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'3.8'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">pg18</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> registry.highgo.com/postgres/postgres</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">18.4</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">container_name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> pg18</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">restart</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> always</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"5435:5432"</span><span class="token plain">          </span><span class="token comment" style="color:#999988;font-style:italic"># 宿主机5435，避开5432/5433占用</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">environment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">POSTGRES_USER</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> postgres</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">POSTGRES_PASSWORD</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> PGtest@2026</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">POSTGRES_DB</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> testdb</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># ⚠️ PG18+挂载根目录 /var/lib/postgresql，非 /data 子目录</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./pgdata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/var/lib/postgresql</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">networks</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">pg_net</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">networks</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">pg_net</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">driver</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> bridge</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804760199-05c508e5-f647-47b1-aa6d-fae49fa97609.png" alt="" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="45-修正后重新启动">4.5 修正后重新启动<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#45-%E4%BF%AE%E6%AD%A3%E5%90%8E%E9%87%8D%E6%96%B0%E5%90%AF%E5%8A%A8" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server pg-compose]# docker-compose down</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[+] Running 2/2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ✔ Container pg18             Removed                                                0.0s </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ✔ Network pg-compose_pg_net  Removed                                                0.2s </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server pg-compose]# vim docker-compose.yml   # 端口改为5435</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server pg-compose]# docker-compose up -d</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[+] Running 2/2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ✔ Network pg-compose_pg_net  Created                                                0.2s </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ✔ Container pg18             Started                                                0.4s </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server pg-compose]#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804760830-b11d7d95-c543-4672-bb33-c2b25c42df19.png" alt="" class="img_ev3q"></p>
<p>查看日志确认成功：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server pg-compose]# docker logs pg18 | grep "ready to accept"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-27 10:31:46.188 UTC [1] LOG:  database system is ready to accept connections</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="46-验证容器运行状态">4.6 验证容器运行状态<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#46-%E9%AA%8C%E8%AF%81%E5%AE%B9%E5%99%A8%E8%BF%90%E8%A1%8C%E7%8A%B6%E6%80%81" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server pg-compose]# docker ps | grep pg18</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">1d53e4097a3d   registry.highgo.com/postgres/postgres:18.4-bookworm   "docker-entrypoint.s…"   30 seconds ago   Up 29 seconds   0.0.0.0:5435-&gt;5432/tcp, :::5435-&gt;5432/tcp                                                        pg18</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">0ea9e62cd3d0   registry.highgo.com/postgres/postgres:18.4-bookworm   "docker-entrypoint.s…"   35 minutes ago   Up 35 minutes   0.0.0.0:5433-&gt;5432/tcp, :::5433-&gt;5432/tcp                                                        pg184</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server pg-compose]#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804760473-4f5ff67f-01be-413e-9af0-cf5813262467.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="五-postgresql-184-基础-crud-实操验证">五、🔍 PostgreSQL 18.4 基础 CRUD 实操验证<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#%E4%BA%94-postgresql-184-%E5%9F%BA%E7%A1%80-crud-%E5%AE%9E%E6%93%8D%E9%AA%8C%E8%AF%81" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>容器日志输出 <code>database system is ready to accept connections</code> 代表数据库完全初始化完毕，直接进入容器完成版本校验与增删改查全流程验证。</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="51-进入数据库终端--版本校验">5.1 进入数据库终端 &amp; 版本校验<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#51-%E8%BF%9B%E5%85%A5%E6%95%B0%E6%8D%AE%E5%BA%93%E7%BB%88%E7%AB%AF--%E7%89%88%E6%9C%AC%E6%A0%A1%E9%AA%8C" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server pg-compose]# docker exec -it pg18 psql -U postgres</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">psql (18.4 (Debian 18.4-1.pgdg12+1))</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Type "help" for help.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">postgres=# select version();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                                                 version                                                              </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">------------------------------------------------------------------------------------------------------------------------------------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> PostgreSQL 18.4 (Debian 18.4-1.pgdg12+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14+deb12u1) 12.2.0, 64-bit</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">(1 row)</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804760545-2437c33a-08a0-4996-9a6a-539a73cf4f92.png" alt="" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="52-完整-crud-示例">5.2 完整 CRUD 示例<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#52-%E5%AE%8C%E6%95%B4-crud-%E7%A4%BA%E4%BE%8B" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 1. 创建测试业务表</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> test_user </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    id BIGSERIAL </span><span class="token keyword" style="color:#00009f">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    username </span><span class="token keyword" style="color:#00009f">VARCHAR</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">50</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">NOT</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    age </span><span class="token keyword" style="color:#00009f">INT</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    create_time </span><span class="token keyword" style="color:#00009f">TIMESTAMP</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">DEFAULT</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">now</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<div class="language-plain codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-plain codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">postgres=# CREATE TABLE test_user (</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    id BIGSERIAL PRIMARY KEY,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    username VARCHAR(50) NOT NULL,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    age INT,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    create_time TIMESTAMP DEFAULT now()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">CREATE TABLE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">postgres=#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804760896-63d841a6-ef5a-4da7-aab3-317786fb17da.png" alt="" class="img_ev3q"></p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 2. 新增多条测试数据</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> test_user </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">username</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">age</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'zhangsan'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token number" style="color:#36acaa">24</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'lisi'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token number" style="color:#36acaa">28</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'wangwu'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token number" style="color:#36acaa">32</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<div class="language-plain codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-plain codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">postgres=# INSERT INTO test_user (username,age) VALUES </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">('zhangsan',24),</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">('lisi',28),</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">('wangwu',32);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">INSERT 0 3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">postgres=#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804761022-6e50a131-5ae2-49ec-9556-38a820a74114.png" alt="" class="img_ev3q"></p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 3. 全表查询</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> test_user</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<div class="language-plain codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-plain codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">postgres=# SELECT * FROM test_user;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> id | username | age |        create_time         </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">----+----------+-----+----------------------------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  1 | zhangsan |  24 | 2026-06-27 12:29:53.710586</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  2 | lisi     |  28 | 2026-06-27 12:29:53.710586</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  3 | wangwu   |  32 | 2026-06-27 12:29:53.710586</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">(3 rows)</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804761001-4cf4045d-d7d5-4929-b61d-02f7c1a780ac.png" alt="" class="img_ev3q"></p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 4. 条件更新数据</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">UPDATE</span><span class="token plain"> test_user </span><span class="token keyword" style="color:#00009f">SET</span><span class="token plain"> age</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">29</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">WHERE</span><span class="token plain"> username</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'lisi'</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<div class="language-plain codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-plain codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">postgres=# UPDATE test_user SET age=29 WHERE username='lisi';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">UPDATE 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">postgres=#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804761173-6d7bf770-d079-46bb-9e08-41e1d9cddca7.png" alt="" class="img_ev3q"></p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 5. 确认更新结果</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> test_user</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<div class="language-plain codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-plain codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">postgres=# SELECT * FROM test_user;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> id | username | age |        create_time         </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">----+----------+-----+----------------------------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  1 | zhangsan |  24 | 2026-06-27 12:29:53.710586</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  3 | wangwu   |  32 | 2026-06-27 12:29:53.710586</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  2 | lisi     |  29 | 2026-06-27 12:29:53.710586</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">(3 rows)</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804761436-1be4cc98-384e-49de-b28f-669351cc7ae7.png" alt="" class="img_ev3q"></p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 6. 删除指定行数据</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">DELETE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> test_user </span><span class="token keyword" style="color:#00009f">WHERE</span><span class="token plain"> username</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'wangwu'</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<div class="language-plain codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-plain codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">postgres=# DELETE FROM test_user WHERE username='wangwu';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">DELETE 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">postgres=#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804761443-5d55acd7-d804-4f77-9b90-c5df2f7c435a.png" alt="" class="img_ev3q"></p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 7. 最终结果验证</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> test_user</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<div class="language-plain codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-plain codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">postgres=# SELECT * FROM test_user;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> id | username | age |        create_time         </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">----+----------+-----+----------------------------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  1 | zhangsan |  24 | 2026-06-27 12:29:53.710586</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  2 | lisi     |  29 | 2026-06-27 12:29:53.710586</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">(2 rows)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">postgres=# \q</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server pg-compose]#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804761593-7140d70b-5280-4616-9de4-1f94667e5bb9.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="六-连接端口区分说明">六、📊 连接端口区分说明<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#%E5%85%AD-%E8%BF%9E%E6%8E%A5%E7%AB%AF%E5%8F%A3%E5%8C%BA%E5%88%86%E8%AF%B4%E6%98%8E" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<table><thead><tr><th>部署方式</th><th>外部连接端口</th><th>数据库类型</th></tr></thead><tbody><tr><td>IvorySQL 容器</td><td><code>5432</code> / <code>1521</code></td><td>IvorySQL 5.3</td></tr><tr><td>单机 run 部署</td><td><code>5433</code></td><td>PostgreSQL 18.4</td></tr><tr><td>Compose 部署</td><td><code>5435</code></td><td>PostgreSQL 18.4</td></tr></tbody></table>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Compose 容器内免密登录（不受宿主机端口影响）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server pg-compose]# docker exec -it pg18 psql -U postgres</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">psql (18.4 (Debian 18.4-1.pgdg12+1))</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Type "help" for help.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">postgres=#</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://cdn.nlark.com/yuque/0/2026/png/26769700/1782804761636-7a0b3605-2647-4665-aef6-625ff2624a4d.png" alt="" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="七-实测高频踩坑汇总翻车记录">七、🧾 实测高频踩坑汇总（翻车记录）<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#%E4%B8%83-%E5%AE%9E%E6%B5%8B%E9%AB%98%E9%A2%91%E8%B8%A9%E5%9D%91%E6%B1%87%E6%80%BB%E7%BF%BB%E8%BD%A6%E8%AE%B0%E5%BD%95" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="坑-1简写-tag-直接拉取提示镜像不存在">坑 1：简写 Tag 直接拉取，提示镜像不存在<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#%E5%9D%91-1%E7%AE%80%E5%86%99-tag-%E7%9B%B4%E6%8E%A5%E6%8B%89%E5%8F%96%E6%8F%90%E7%A4%BA%E9%95%9C%EF%BF%BD%E5%83%8F%E4%B8%8D%E5%AD%98%E5%9C%A8" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<ul>
<li class=""><strong>现象</strong>：<code>postgres:18.4</code> 拉取失败</li>
<li class=""><strong>解决</strong>：仓库内 PG 镜像的完整路径是 <code>postgres/postgres</code>，不是 <code>postgres</code>；且标签必须带 <code>-bookworm</code> 发行版后缀</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="坑-2shell-历史扩展--导致命令错乱">坑 2：Shell 历史扩展 <code>!!</code> 导致命令错乱<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#%E5%9D%91-2shell-%E5%8E%86%E5%8F%B2%E6%89%A9%E5%B1%95--%E5%AF%BC%E8%87%B4%E5%91%BD%E4%BB%A4%E9%94%99%E4%B9%B1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<ul>
<li class=""><strong>现象</strong>：密码带 <code>!!</code> 后命令被"展开"，报 <code>Unable to find image 'run:latest'</code></li>
<li class=""><strong>解决</strong>：密码用单引号包起来，或用不带 <code>!</code> 的密码</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="坑-3端口-5432-被-ivorysql-占用">坑 3：端口 5432 被 IvorySQL 占用<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#%E5%9D%91-3%E7%AB%AF%E5%8F%A3-5432-%E8%A2%AB-ivorysql-%E5%8D%A0%E7%94%A8" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<ul>
<li class=""><strong>现象</strong>：<code>Bind for 0.0.0.0:5432 failed: port is already allocated</code></li>
<li class=""><strong>解决</strong>：换端口映射，如 <code>5433:5432</code> 或 <code>5435:5432</code></li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="坑-4pg18-挂载路径变更">坑 4：PG18+ 挂载路径变更<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#%E5%9D%91-4pg18-%E6%8C%82%E8%BD%BD%E8%B7%AF%E5%BE%84%E5%8F%98%E6%9B%B4" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<ul>
<li class=""><strong>现象</strong>：容器启动失败，日志提示 <code>unused mount/volume</code></li>
<li class=""><strong>解决</strong>：挂载 <code>/var/lib/postgresql</code>，<strong>不是</strong> <code>/var/lib/postgresql/data</code></li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="坑-5宿主机无-psql-客户端">坑 5：宿主机无 psql 客户端<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#%E5%9D%91-5%E5%AE%BF%E4%B8%BB%E6%9C%BA%E6%97%A0-psql-%E5%AE%A2%E6%88%B7%E7%AB%AF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<ul>
<li class=""><strong>现象</strong>：<code>-bash: psql: 未找到命令</code></li>
<li class=""><strong>解决</strong>：统一用 <code>docker exec -it pg18 psql -U postgres</code> 进入容器</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="八-实测体验客观点评">八、💭 实测体验客观点评<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#%E5%85%AB-%E5%AE%9E%E6%B5%8B%E4%BD%93%E9%AA%8C%E5%AE%A2%E8%A7%82%E7%82%B9%E8%AF%84" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>很多人觉得国内镜像只是单纯提速工具，其实对 PG 迁移场景价值更大：</p>
<p>✅ <strong>优势：</strong></p>
<ol>
<li class="">一套仓库同时拥有 IvorySQL + 原生 PG 镜像，做 Oracle 语法兼容对比、数据库迁移评估不用切换镜像源，环境搭建效率翻倍；</li>
<li class="">PG 18.4 同步最新社区内核修复，同步速度和海外 Docker Hub 差距极小，中小企业不用搭建私有镜像仓库；</li>
<li class="">镜像完全官方原生无二次修改，信创、政企测试环境合规，规避第三方镜像篡改风险。</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="总结">总结<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#%E6%80%BB%E7%BB%93" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>这次完整实测 <code>registry.highgo</code> 仓库 PostgreSQL 18.4-bookworm 镜像，整套流程从拉取、单机部署、生产 Compose 编排到排坑全部落地。</p>
<p><strong>核心优势总结：</strong> 国内节点高速拉取、原生官方镜像安全合规、搭配 IvorySQL 一站式完成迁移对比测试。</p>
<p><strong>实操重点提醒：</strong> 拉取镜像务必携带 <code>-bookworm</code> 后缀，必须挂载宿主机数据目录防止磁盘爆满，复制命令优先单行避免参数错乱。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-作者注">📋 作者注<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#-%E4%BD%9C%E8%80%85%E6%B3%A8" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>本文所有操作及测试均基于 <strong>openEuler 操作系统环境</strong>完成，核心围绕 IvorySQL 社区镜像仓库 <code>registry.highgo.com</code> 内 <strong>PostgreSQL 18.4-bookworm</strong> 兼容性验证展开。</p>
<p>请注意 IvorySQL 社区 PostgreSQL 镜像迭代较快，文中配置参数、命令行输出可能随新版本更新调整。生产部署前，请以 IvorySQL 官方文档、<code>registry.highgo</code> 仓库最新 Tag 清单为准。</p>
<p>以上仅为个人实践总结，不代表社区官方观点，文中命令、密码仅作演示，线上环境请自定义高强度密码。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-作者简介">👤 作者简介<a href="https://ivorysql.org/zh-cn/blog/pg18-mirror#-%E4%BD%9C%E8%80%85%E7%AE%80%E4%BB%8B" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<blockquote>
<p><strong>作者：</strong> ShunWah<br>
<strong>公众号：</strong> "shunwah星辰数智社"主理人。</p>
<p><strong>持有认证：</strong> OceanBase、MySQL、OpenGauss、崖山、金仓KingBase、KaiwuDB、亚信AntDBCA、翰高、GBase、Galaxybase、Neo4j、NebulaGraph、东方通TongTech、TiDB 等多项权威认证。</p>
<p><strong>获奖经历：</strong> 崖山YashanDB YVP、浪潮KaiwuDB MVP、墨天轮 MVP、金仓社区KVA、TiDB社区MVA、NebulaGraph社区之星、IFClub星珩联盟·智库星系技术专家、ITPUB 技术专家、GBase 8a开发者联盟成员&nbsp;社区版主及布道师。在OceanBase&amp;墨天轮征文大赛、OpenGauss、TiDB、YashanDB、Kingbase、KWDB、Navicat 征文等赛事中多次斩获一、二、三等奖，原创技术文章常年被墨天轮、CSDN、ITPUB 等平台首页推荐。</p>
</blockquote>]]></content:encoded>
            <category>IvorySQL</category>
            <category>PostgreSQL</category>
            <category>Docker</category>
            <category>Mirror</category>
            <category>PG18</category>
            <category>openEuler</category>
        </item>
        <item>
            <title><![CDATA[IvorySQL Agent 探索与实践]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/ivorysql-agent</link>
            <guid>https://ivorySQL.org/zh-cn/blog/ivorysql-agent</guid>
            <pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[本文基于 HOW 2026 Oreo Yang 的演讲内容整理。]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>本文基于 HOW 2026 Oreo Yang 的演讲内容整理。
视频回放：<a href="https://www.bilibili.com/video/BV1CFLB6DEHV/" target="_blank" rel="noopener noreferrer" class="">https://www.bilibili.com/video/BV1CFLB6DEHV/</a></p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="一项目背景">一、项目背景<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#%E4%B8%80%E9%A1%B9%E7%9B%AE%E8%83%8C%E6%99%AF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-什么是-ivorysql">1.1 什么是 IvorySQL<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#11-%E4%BB%80%E4%B9%88%E6%98%AF-ivorysql" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL 是一个基于 PostgreSQL 并兼容 Oracle 的开源关系型数据库项目，致力于提供灵活、高性能的数据管理解决方案。其核心特性包括：</p>
<ul>
<li class=""><strong>Oracle 兼容</strong>：支持 Oracle 数据类型、PL/SQL 语法、内置函数及 Package 包，显著降低从 Oracle 迁移或混合部署的技术门槛与成本。</li>
<li class=""><strong>社区生态</strong>：拥有活跃的开源社区，通过社区协作持续推动产品迭代与技术创新。</li>
<li class=""><strong>部署灵活性</strong>：支持传统部署、容器化、云服务、在线体验、沙盒环境等多种部署方式。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-项目背景与目标">1.2 项目背景与目标<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#12-%E9%A1%B9%E7%9B%AE%E8%83%8C%E6%99%AF%E4%B8%8E%E7%9B%AE%E6%A0%87" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>数据库运维面临多重挑战：指标分散、诊断困难、人力成本高、文档查找繁琐。IvorySQL Agent 的核心理念是利用大模型技术实现智能对话与自动化分析，构建一站式监控、诊断、运维辅助平台。项目要解决的六大核心问题包括：智能文档检索、数据库运维、性能调优、SQL 编写效率提升、企业知识管理，最终通过 AI 技术实现从"被动检索"到"主动服务"的转型。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-什么是-agent">1.3 什么是 Agent<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#13-%E4%BB%80%E4%B9%88%E6%98%AF-agent" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>Agent = 大模型 + 工具 + 推理。智能体将语言模型与工具能力结合，能够对任务进行推理、决定使用哪些工具，并通过迭代寻求解决方案。</p>
<p>IvorySQL Agent 的技术选型：</p>
<ul>
<li class=""><strong>模型层</strong>：支持 OpenAI GPT 系列、Anthropic Claude 系列、智谱 AI 等多模型后端</li>
<li class=""><strong>工具层</strong>：21 个数据库工具，覆盖 7 大领域专家</li>
<li class=""><strong>框架层</strong>：基于 LangGraph 工作流编排，实现 ReAct 循环迭代</li>
<li class=""><strong>知识层</strong>：矩阵式知识库，覆盖多版本、多模式</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="二系统架构">二、系统架构<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#%E4%BA%8C%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-整体架构">2.1 整体架构<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#21-%E6%95%B4%E4%BD%93%E6%9E%B6%E6%9E%84" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>系统采用四层架构设计：</p>
<table><thead><tr><th>层级</th><th>技术栈</th><th>职责</th></tr></thead><tbody><tr><td>Web 接入层</td><td>FastAPI + 静态资源 + CORS</td><td>用户界面交互</td></tr><tr><td>智能路由层</td><td>LangGraph 工作流 + 状态管理</td><td>意图识别与请求分发</td></tr><tr><td>专家 Agent 层</td><td>NL2SQL / Analysis / Ops / Backup / Install / Knowledge / General</td><td>领域任务处理</td></tr><tr><td>工具与存储层</td><td>数据库工具集 + LLM 接口 + 向量存储 + 时序数据库</td><td>数据访问与工具执行</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-核心数据流">2.2 核心数据流<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#22-%E6%A0%B8%E5%BF%83%E6%95%B0%E6%8D%AE%E6%B5%81" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p><strong>监控采集流</strong>：通过 Collector 设计，覆盖 50+ 监控指标（连接、缓存、锁、事务、查询、表膨胀等）。针对 IvorySQL 的 Oracle 兼容模式，动态注册自定义类型编解码器。采集数据以 JSONB 格式存储于 PostgreSQL，后台采用 APScheduler 每 30 秒执行一次采集任务，并通过 Delta 增量计算减少开销。</p>
<p><strong>智能对话流</strong>：用户输入经路由分发至对应 Agent，构建上下文后调用工具链执行推理，最终生成回答并返回用户。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-异常检测规则引擎--llm-协同">2.3 异常检测：规则引擎 + LLM 协同<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#23-%E5%BC%82%E5%B8%B8%E6%A3%80%E6%B5%8B%E8%A7%84%E5%88%99%E5%BC%95%E6%93%8E--llm-%E5%8D%8F%E5%90%8C" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>异常检测采用规则引擎与 LLM 协同工作的双轨机制：</p>
<ul>
<li class=""><strong>规则引擎</strong>：预定义阈值规则，命中后自动触发 LLM 分析</li>
<li class=""><strong>LLM 分析</strong>：支持 Claude/OpenAI/智谱 AI 等多模型，对异常进行智能解读</li>
<li class=""><strong>向量化存储</strong>：512 维本地向量（支持 256~2048 维自定义），配合 pgvector 索引加速检索</li>
<li class=""><strong>历史学习</strong>：通过相似案例检索复用诊断经验</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="三关键技术实现">三、关键技术实现<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#%E4%B8%89%E5%85%B3%E9%94%AE%E6%8A%80%E6%9C%AF%E5%AE%9E%E7%8E%B0" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-两阶段智能路由">3.1 两阶段智能路由<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#31-%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%99%BA%E8%83%BD%E8%B7%AF%E7%94%B1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class=""><strong>Phase 1（确定性路由）</strong>：基于关键词与正则表达式匹配，响应时间约 0ms，覆盖约 70% 的场景</li>
<li class=""><strong>Phase 2（LLM 分类）</strong>：规则匹配失败时调用大模型进行意图识别，响应时间约 300ms</li>
<li class=""><strong>Post-Confirm</strong>：短路复用机制，0ms 完成缓存命中</li>
</ul>
<p>关键设计决策：关键词互斥防止跨域冲突、中英双语正则、渐进降级（关键词 → LLM → general 三层兜底）、模式感知根据 compat_mode 动态裁剪工具表、LLM 容错（指数退避重试 ×2，超时降级 general）、域隔离防污染。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-本地向量检索零外部依赖">3.2 本地向量检索（零外部依赖）<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#32-%E6%9C%AC%E5%9C%B0%E5%90%91%E9%87%8F%E6%A3%80%E7%B4%A2%E9%9B%B6%E5%A4%96%E9%83%A8%E4%BE%9D%E8%B5%96" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>采用 BAAI/bge-small-zh-v1.5 嵌入模型，512 维，完全离线运行。pgvector 扩展 + IVFFlat 索引加速。知识库来源包括 IvorySQL 官方文档、PostgreSQL 官方文档和历史分析结果。RAG 检索采用"加权向量搜索 + BM25 重排 + MMR 多样化"组合方案。切片管理按文档章节划分，包含版本和模式标记防止跨版本知识污染。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-上下文管理与智能压缩">3.3 上下文管理与智能压缩<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#33-%E4%B8%8A%E4%B8%8B%E6%96%87%E7%AE%A1%E7%90%86%E4%B8%8E%E6%99%BA%E8%83%BD%E5%8E%8B%E7%BC%A9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class="">Schema 缓存：1 小时 TTL，DDL 后自动失效</li>
<li class="">包定义缓存：IvorySQL Oracle 模式包信息</li>
<li class="">实时指标：动态加载当前会话、锁、慢查询</li>
<li class="">历史异常：RAG 检索相关案例，top-K=5</li>
</ul>
<p>智能压缩机制应对 15+ 轮长对话：DDL 判断 → 选择性保留（DDL + 最近 6 轮）→ Token 检查 → LLM 摘要生成 → 注入优化上下文。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-工具系统与安全控制">3.4 工具系统与安全控制<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#34-%E5%B7%A5%E5%85%B7%E7%B3%BB%E7%BB%9F%E4%B8%8E%E5%AE%89%E5%85%A8%E6%8E%A7%E5%88%B6" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>21 个内置工具，分领域管理。SQL 验证四层防护：Prompt 预过滤（拦截 80% 无效 SQL）→ 语法缓存校验 → 预确认验证 → 错误反馈重试（最多 3 次）。安全认证采用 API Token + SHA256 + 配置热重载。审计系统记录操作日志与错误追踪。IvorySQL Oracle/PG 模式工具差异化。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-chat-流程规划--生成--验证--流式输出">3.5 Chat 流程：规划 · 生成 · 验证 · 流式输出<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#35-chat-%E6%B5%81%E7%A8%8B%E8%A7%84%E5%88%92--%E7%94%9F%E6%88%90--%E9%AA%8C%E8%AF%81--%E6%B5%81%E5%BC%8F%E8%BE%93%E5%87%BA" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>Plan 规划由 LLM 生成 JSON 格式步骤计划。SSE 流式输出实时推送 token、tool_start、tool_end 事件。状态往返保证多步计划跨轮次连贯性，无需重建上下文。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="36-部署架构">3.6 部署架构<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#36-%E9%83%A8%E7%BD%B2%E6%9E%B6%E6%9E%84" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>Docker 容器化设计：服务拆分（存储 PostgreSQL + Agent 服务 + Grafana 可视化 + knowledge 批量导入）、settings.json 运行时配置热更新、多目标支持监控多个实例、环境隔离、docker-compose 一键启动。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="四技术��特色">四、技术特色<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#%E5%9B%9B%E6%8A%80%E6%9C%AF%E7%89%B9%E8%89%B2" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<ul>
<li class="">纯离线本地运行，无需外部 API</li>
<li class="">多 RAG 领域扩展（当前 7 个 Agent，可继续扩展）</li>
<li class="">智能上下文缓存与压缩机制</li>
<li class="">多层次安全校验与审计日志</li>
<li class="">模块化可扩展设计，支持自定义工具与知识库</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="五应用场景">五、应用场景<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#%E4%BA%94%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<table><thead><tr><th>场景</th><th>用户问题</th><th>处理 Agent</th></tr></thead><tbody><tr><td>连接数异常诊断</td><td>"为什么连接数这么高？"</td><td>Ops Agent 分析活跃会话、长事务</td></tr><tr><td>慢查询优化</td><td>"帮我找出最慢的查询"</td><td>NL2SQL Agent 检索慢查询日志</td></tr><tr><td>备份配置查询</td><td>"备份配置正常吗？"</td><td>Backup Agent 检查 WAL 归档</td></tr><tr><td>知识库问答</td><td>"如何使用 %rowtype？"</td><td>Knowledge Agent 检索 RAG 知识库</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="六未来规划">六、未来规划<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#%E5%85%AD%E6%9C%AA%E6%9D%A5%E8%A7%84%E5%88%92" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="61-短期规划">6.1 短期规划<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#61-%E7%9F%AD%E6%9C%9F%E8%A7%84%E5%88%92" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class="">完善智能 Agent 框架，支持多 Model 后端与流式输出</li>
<li class="">实现 IvorySQL 与 PostgreSQL 双模式兼容</li>
<li class="">多模态输入支持：文件分析、截图识别、语音输入</li>
<li class="">知识库管理：在线新增、编辑、删除知识条目与切片</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="62-长期规划">6.2 长期规划<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#62-%E9%95%BF%E6%9C%9F%E8%A7%84%E5%88%92" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p><strong>功能扩展</strong>：多模式多版本支持、自动化修复、多实例管理、安全与访问控制、告警通知集成、自学习能力、更多领域 Agent。</p>
<p><strong>愿景</strong>：原生集成 pgvector 成为 Agent 首选向量存储、多语言 SDK 开箱即用、Agent 开发套件、一键 Docker Compose 部署完整环境。核心能力：3 行代码实现 RAG 应用，无需配置外部向量服务。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="七总结">七、总结<a href="https://ivorysql.org/zh-cn/blog/ivorysql-agent#%E4%B8%83%E6%80%BB%E7%BB%93" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL Agent 是 IvorySQL 社区在 AI 赋能数据库管理领域的一次系统性探索。项目基于 ReAct 框架与 LangGraph 工作流编排，构建了覆盖路由、检索、上下文管理、工具调用和安全审计的完整技术体系，实现了从自然语言到数据库运维动作的端到端自动化。未来，项目将持续向多模态交互、自动化修复、SDK 生态等方向演进，降低数据库的使用门槛，提升运维效率，让 IvorySQL 不仅是优秀的数据库，更成为智能数据管理的基础设施。</p>]]></content:encoded>
            <category>IvorySQL</category>
            <category>AI</category>
            <category>Agent</category>
            <category>RAG</category>
            <category>NL2SQL</category>
            <category>PostgreSQL</category>
        </item>
        <item>
            <title><![CDATA[Oracle 为什么能跑在 PostgreSQL 上？答案藏在这套内核里]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/oracle-compat-kernel</link>
            <guid>https://ivorySQL.org/zh-cn/blog/oracle-compat-kernel</guid>
            <pubDate>Mon, 06 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[这几年，越来越多企业开始认真评估从 Oracle 迁移到 PostgreSQL。迁移的原因显而易见，商业数据库授权成本高，厂商绑定重，国产化和开源替代压力也在增加。PostgreSQL 本身足够成熟，生态也足够活跃，那么从 Oracle 迁移到 PG 看起来似乎是顺理成章的。但真正进到迁移项目里，大家很快会发现，难点往往不在数据迁移，而在 Oracle 长年给用户养成的数据库使用习惯。]]></description>
            <content:encoded><![CDATA[<p>这几年，越来越多企业开始认真评估从 Oracle 迁移到 PostgreSQL。迁移的原因显而易见，商业数据库授权成本高，厂商绑定重，国产化和开源替代压力也在增加。PostgreSQL 本身足够成熟，生态也足够活跃，那么从 Oracle 迁移到 PG 看起来似乎是顺理成章的。但真正进到迁移项目里，大家很快会发现，难点往往不在数据迁移，而在 Oracle 长年给用户养成的数据库使用习惯。</p>
<p>一个老系统里，Oracle 通常不只是存表。大量业务逻辑写在存储过程、函数、触发器和 Package 里；SQL 里到处是 <code>ROWNUM</code>、<code>SYSDATE</code>、<code>DUAL</code>、<code>NUMBER</code>、<code>VARCHAR2</code>；应用和报表工具会查 <code>ALL_TAB_COLUMNS</code>、<code>DBA_PROCEDURES</code> 这类数据字典；还有空字符串等于 NULL、隐式类型转换、大小写规则、NLS 参数这些平时不显眼但十分影响结果的细节。</p>
<p>这就是企业从 Oracle 迁到 PG 时最痛苦的地方：如果完全按 PostgreSQL 语法进行改造，应用改造量会很大，测试周期会被拉长，业务风险也难控制；如果只在外层做 SQL 改写，又很难覆盖存储过程、包、数据字典。</p>
<p>IvorySQL 的出现完美解决了这个问题，它是 PG 生态中高度兼容 Oracle 的内核，并且紧跟社区最新版本，最重要的是，它开源免费。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="一ivorysql-是什么">一、IvorySQL 是什么<a href="https://ivorysql.org/zh-cn/blog/oracle-compat-kernel#%E4%B8%80ivorysql-%E6%98%AF%E4%BB%80%E4%B9%88" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL 是基于 PostgreSQL 的开源数据库项目，目标是在保留 PG 内核能力和生态兼容性的基础上，提供更深入的 Oracle 兼容能力。它不是重新写一个数据库，也不是在应用层做 SQL 字符串替换，而是在 PostgreSQL 内核、扩展和系统对象层面补齐 Oracle 应用迁移时经常依赖的行为。</p>
<p>从项目结构看，IvorySQL 的兼容范围不只在 SQL 语法。它包括 Oracle 模式入口、独立 Oracle parser、Oracle 数据类型、隐式转换、PL/iSQL、Package、内置函数、内置包、系统视图、<code>ROWNUM</code>、<code>ROWID</code>、<code>FORCE VIEW</code>、MERGE 语义、序列行为，以及 NLS、空字符串、标识符大小写等会话级差异。</p>
<p>这也是它和普通兼容层最大的区别。IvorySQL 更关注"你的应用能不能按原来的业务代码运行"。企业迁移 Oracle 时，表数据迁移通常不是最难的那部分，难的是数据库里那些多年积累的存储过程、包、隐式转换和数据字典依赖。IvorySQL 正是把这些问题统统归纳到数据库内核中进行处理，让业务层尽量少动刀子。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="二从源码看-ivorysql-是怎么兼容-oracle-的">二、从源码看 IvorySQL 是怎么兼容 Oracle 的<a href="https://ivorysql.org/zh-cn/blog/oracle-compat-kernel#%E4%BA%8C%E4%BB%8E%E6%BA%90%E7%A0%81%E7%9C%8B-ivorysql-%E6%98%AF%E6%80%8E%E4%B9%88%E5%85%BC%E5%AE%B9-oracle-%E7%9A%84" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL 的兼容入口，首先在模式隔离上。</p>
<p><code>src/include/utils/ora_compatible.h</code> 定义了数据库类型和 parser 类型。数据库模式有 <code>DB_PG</code> 和 <code>DB_ORACLE</code>，解析模式有 <code>PG_PARSER</code> 和 <code>ORA_PARSER</code>。Oracle 模式下还定义了专门的搜索路径：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token macro property directive-hash" style="color:#36acaa">#</span><span class="token macro property directive keyword" style="color:#00009f">define</span><span class="token macro property" style="color:#36acaa"> </span><span class="token macro property macro-name" style="color:#36acaa">ORA_SEARCH_PATH</span><span class="token macro property" style="color:#36acaa"> </span><span class="token macro property string" style="color:#e3116c">"sys,\"$user\", public"</span><br></span></code></pre></div></div>
<p><code>sys</code> 放在前面，是为了让 Oracle 兼容对象优先被找到。比如 <code>NUMBER</code>、<code>SYSDATE</code>、<code>DBMS_OUTPUT</code>、<code>ALL_TAB_COLUMNS</code> 这些对象，在迁移应用里往往会直接出现。如果搜索路径不对，后面的类型、函数、包和数据字典视图就会变成零散补丁。</p>
<p>模式切换的核心在 <code>src/backend/utils/misc/ivy_guc.c</code>。这里有两个重要变量：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">int</span><span class="token plain"> database_mode </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> DB_PG</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">int</span><span class="token plain"> compatible_db </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> PG_PARSER</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p><code>ivorysql.database_mode</code> 记录当前数据库按 PG 还是 Oracle 使用，<code>ivorysql.compatible_mode</code> 决定当前会话用 PG parser 还是 Oracle parser。<code>check_compatible_mode()</code> 会阻止不合法切换。比如普通 PG 数据库不能随意打开 Oracle parser；进入 Oracle 模式之前，还要确认 <code>ivorysql_ora</code> 兼容库已经加载，并且能找到 <code>ora_raw_parser</code>。</p>
<p>真正切换 parser 的动作在 <code>assign_compatible_mode()</code>。进入 Oracle 模式时，它把 <code>sql_raw_parser</code> 指向 <code>ora_raw_parser</code>；切回 PG 模式时，再恢复到 <code>standard_raw_parser</code>。同一段逻辑还会切换 MERGE 相关 hook，并调整 search_path。</p>
<p>连接层也配合了这套设计。<code>src/backend/main/main.c</code> 的帮助信息里有两类端口：<code>-p PORT</code> 对应 PostgreSQL 模式，<code>-o PORT</code> 对应 Oracle 模式。<code>postmaster.c</code> 解析 <code>-o</code> 参数，<code>backend_startup.c</code> 根据连接端口设置 <code>connmode</code>，<code>postgres.c</code> 里的 <code>InitIvorysql()</code> 再根据连接模式设置 <code>ivorysql.compatible_mode</code>。同一个内核可以接 PG 客户端，也可以接 Oracle 迁移客户端，但两边从会话入口就已经分开。</p>
<p>第二个核心点是双 parser。<code>src/backend/parser/parser.c</code> 里有这两个指针：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">raw_parser_hook_type sql_raw_parser </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> standard_raw_parser</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">raw_parser_hook_type ora_raw_parser </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p><code>raw_parser()</code> 不再固定调用 PostgreSQL parser，而是调用 <code>(*sql_raw_parser)</code>。默认情况下，它指向 <code>standard_raw_parser</code>；进入 Oracle 模式后，指针切到 <code>ora_raw_parser</code>。Oracle parser 的注册在 <code>src/backend/oracle_parser/liboracle_parser.c</code>，模块初始化时 <code>_PG_init()</code> 会把 <code>ora_raw_parser</code> 设置为 <code>oracle_raw_parser</code>。<code>oracle_raw_parser()</code> 使用 Oracle 自己的 scanner、关键字表和 grammar。</p>
<p>这个选择很重要。Oracle SQL 和 PostgreSQL SQL 差别不小，尤其是匿名块、PL/SQL、Package、过程调用、类型声明这些地方。如果把 Oracle 语法全部硬塞进 PG 原来的 grammar，短期可能能快速堆功能，长期维护会非常痛苦。IvorySQL 让两套 parser 在 raw parse 阶段分流，后面再进入 PostgreSQL 的分析、重写、优化和执行框架。它没有重写一个数据库内核，而是把 Oracle 语法接到 PG 内核的后半段继续处理。</p>
<p>语义分析阶段，IvorySQL 开始处理那些不能只靠 parser 解决的问题。<code>ROWNUM</code> 是很典型的例子。Oracle 里的 <code>ROWNUM</code> 不是普通列，也不是 PostgreSQL 的窗口函数。它什么时候赋值，和 scan、qual、排序、limit 的时机有关。写一个 <code>rownum()</code> 函数解决不了这个问题。</p>
<p>IvorySQL 在 <code>src/backend/parser/parse_expr.c</code> 中处理未限定的 <code>rownum</code>。Oracle 模式下，表达式里出现 <code>rownum</code> 会生成 <code>RownumExpr</code>，而不是按普通列名解析。执行阶段则在 <code>src/backend/executor/execExprInterp.c</code> 和 <code>src/include/executor/execScan.h</code> 里配合处理：<code>ExecEvalRownum()</code> 从 executor state 读取 <code>es_rownum</code>，scan 时在合适的位置递增；如果 qual 没通过，还要回退计数。包含 rownum 的投影也要特殊处理，否则值的时机会错。</p>
<p>优化器也做了有限改写。<code>src/backend/optimizer/plan/planner.c</code> 会把一部分简单的 <code>ROWNUM &lt;= N</code>、<code>ROWNUM = 1</code>、<code>ROWNUM &lt; N</code> 转成 <code>LIMIT</code>。但代码没有把所有 rownum 条件都改掉，因为复杂查询里 Oracle <code>ROWNUM</code> 和 PostgreSQL <code>LIMIT</code> 的生效时机并不完全等价。这里的原则很实在：能保证语义一致才优化，不能保证就保留原行为。</p>
<p>类型系统是另一块重头戏。IvorySQL 在 <code>src/include/catalog/pg_type.dat</code> 中加入了多种 Oracle 兼容类型，主要放在 <code>sys</code> 命名空间下，例如 <code>number</code>、<code>binary_float</code>、<code>binary_double</code>、<code>blob</code>、<code>clob</code>、<code>raw</code>、<code>long</code>、<code>long_raw</code>、<code>rowid</code>、<code>urowid</code>、<code>yminterval</code>、<code>dsinterval</code>。这些类型不是只有名字。<code>contrib/ivorysql_ora/src/datatype/datatype--1.0.sql</code> 里还补了输入输出函数、typmod、cast、operator、btree/hash/brin opclass、比较函数和排序支持。</p>
<p>类型兼容必须做到这一层才有意义。一个类型如果不能建表、建索引、参与比较、参与排序、作为函数参数传递，在业务里很快就会露馅。Oracle 应用里大量字段和过程参数依赖 <code>NUMBER</code>、<code>RAW</code>、LOB、日期间隔类型，单纯把名字映射成 PG 类型远远不够。</p>
<p>隐式转换也必须处理。<code>contrib/ivorysql_ora/src/datatype/compatible_oracle_precedence.c</code> 实现了 Oracle 数据类型优先级 hook。<code>src/backend/parser/parse_oper.c</code> 在找不到合适操作符时，会在 Oracle 模式下调用这段逻辑。比如字符串参与数值运算时，Oracle 会按自己的规则尝试转成 <code>NUMBER</code>，而 PostgreSQL 原生规则更严格。IvorySQL 只在 Oracle 模式下介入，让迁移 SQL 更接近 Oracle 行为，同时不改变 PG 模式的类型解析习惯。</p>
<p>再往上，是空字符串、大小写和 NLS 这类看起来小但很容易改坏业务结果的差异。Oracle 把空字符串当成 <code>NULL</code>，PostgreSQL 不这么做。IvorySQL 在 <code>ivy_guc.c</code> 里提供了 <code>ivorysql.enable_emptystring_to_NULL</code>。Oracle 未引用标识符通常转大写，PG 则转小写，所以 IvorySQL 又提供 <code>ivorysql.identifier_case_switch</code>，并在 Oracle parser 和标识符引用逻辑里配合。NLS 参数也在 <code>ivy_guc.c</code> 中，包括日期语言、日期格式、时间戳格式、数值字符和长度语义。</p>
<p>PL/iSQL 和 Package 是 Oracle 兼容里最硬的一块。IvorySQL 在 <code>src/pl/plisql/src</code> 下实现 PL/iSQL。系统语言定义在 <code>src/include/catalog/pg_language.dat</code>，<code>plisql</code> 有自己的 call handler、inline handler 和 validator。</p>
<p>Package 没有被当成普通文本存起来。IvorySQL 新增了 <code>pg_package</code> 和 <code>pg_package_body</code> 两张系统 catalog。<code>pg_package</code> 记录包规格，<code>pg_package_body</code> 记录包体，它们有自己的索引和 syscache。这意味着 Package 被纳入 PostgreSQL 的系统对象体系，可以参与对象查找、权限检查、依赖记录和缓存失效。</p>
<p><code>src/backend/parser/parse_package.c</code> 负责 package 里的类型、变量和函数查找。代码里会先判断当前是不是 <code>ORA_PARSER</code>，不是 Oracle 模式时，很多 package 查找不会继续跑。缓存处理在 <code>src/backend/utils/cache/packagecache.c</code>，<code>PackageCache</code> 放在 <code>CacheMemoryContext</code>，并通过 syscache callback 在 package 或 package body 变化时失效，同时联动 plan cache。</p>
<p>兼容对象层面，IvorySQL 通过 <code>ivorysql_ora</code> 扩展和 <code>sys</code> schema 提供了大量 Oracle 函数、包和系统视图。<code>contrib/ivorysql_ora/src/builtin_functions/builtin_functions--1.0.sql</code> 里有 <code>sysdate</code>、<code>systimestamp</code>、<code>add_months</code>、<code>last_day</code>、<code>months_between</code>、<code>next_day</code>、<code>to_date</code>、<code>to_timestamp</code>，也包括正则函数和 <code>substrb</code>、<code>instrb</code> 这类按字节处理的字符串函数。<code>contrib/ivorysql_ora/src/builtin_packages</code> 下有 <code>dbms_output</code>、<code>dbms_lock</code>、<code>dbms_utility</code>、<code>utl_file</code> 等内置包。<code>contrib/ivorysql_ora/src/sysview/sysview--1.0.sql</code> 则提供 <code>SYS.DBA_PROCEDURES</code>、<code>SYS.ALL_TAB_COLUMNS</code> 等 Oracle 风格数据字典视图。</p>
<p>还有一些兼容点分布在更具体的位置。<code>FORCE VIEW</code> 在 <code>src/backend/commands/view.c</code>、<code>parse_relation.c</code>、<code>ruleutils.c</code> 中配合处理，支持先创建暂时无法编译通过的视图，等依赖对象出现后再编译。MERGE 通过 <code>contrib/ivorysql_ora/src/ivorysql_ora.c</code> 注册相关 hook，只在 Oracle 模式下接管部分 transform 和执行逻辑。序列差异在 <code>src/backend/commands/sequence.c</code> 中处理。<code>ROWID</code> 和 <code>UROWID</code> 更底层，还涉及 <code>relhasrowid</code>、tuple descriptor 里的 <code>tdhasrowid</code>，以及执行器和 junk filter。</p>
<p>把这些点连起来看，IvorySQL 的兼容路径不是"在外面糊一层 Oracle 方言"。它把 Oracle 应用真正依赖的行为，放进 PostgreSQL 内核里相对合适的位置：语法交给 parser，类型交给 catalog 和类型推导，Package 交给系统目录和缓存，<code>ROWNUM</code> 交给执行器，数据字典交给系统视图，函数和包交给扩展对象。这种做法工作量大，但边界比简单字符串改写可靠得多。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="三兼容-oracle-为什么不会对性能产生影响">三、兼容 Oracle 为什么不会对性能产生影响<a href="https://ivorysql.org/zh-cn/blog/oracle-compat-kernel#%E4%B8%89%E5%85%BC%E5%AE%B9-oracle-%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E4%BC%9A%E5%AF%B9%E6%80%A7%E8%83%BD%E4%BA%A7%E7%94%9F%E5%BD%B1%E5%93%8D" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>严格说，任何功能只要被使用，就一定有它自己的执行成本。Oracle 模式下使用 Oracle 类型、Oracle 函数、PL/iSQL、Package、LOB、复杂日期格式解析，这些功能不可能完全没有代价。这里说"不会对性能产生影响"，更准确的意思是：IvorySQL 的 Oracle 兼容设计不会让普通 PG 查询平白多跑一层 Oracle 逻辑，也不会把 Oracle 兼容变成所有会话共享的系统性负担。</p>
<p><strong>第一，parser 在会话开始时已经分流。</strong> 普通 PG 会话使用 <code>PG_PARSER</code>，<code>sql_raw_parser</code> 指向 <code>standard_raw_parser</code>；Oracle 会话才会切到 <code>ora_raw_parser</code>。SQL parser 是每条语句都会经过的入口，如果每条 SQL 都先尝试 Oracle 语法，再尝试 PG 语法，成本会非常直接。IvorySQL 没有这么做。它在会话级别确定 parser，PG 语句仍然按 PG parser 解析，Oracle 语句才进入 Oracle parser。</p>
<p><strong>第二，Oracle hook 多数有明确触发条件。</strong> 比如类型优先级 hook 不是每次操作符解析都完整跑一遍 Oracle 规则，而是在找不到合适操作符，并且当前处于 Oracle 模式时才介入。Package 查找也类似，<code>parse_package.c</code> 会先判断 parser 类型，不是 <code>ORA_PARSER</code> 就不继续走 Oracle Package 查找。hook 在代码里存在，不等于所有查询都承担完整兼容逻辑。</p>
<p><strong>第三，Oracle 类型和函数进入 PostgreSQL 原生对象体系以后，执行阶段可以复用 PG 的函数调用、表达式求值、排序、索引访问和缓存机制。</strong> <code>NUMBER</code>、LOB、RAW、ROWID 等类型被放进 catalog，配套 operator、cast、opclass 后，优化器和执行器可以按 PG 熟悉的方式处理它们。Oracle 函数自身有成本，但那是使用功能时的成本，不会转嫁给不使用这些对象的普通 PG 查询。</p>
<p><strong>第四，Package 通过缓存避免重复解析。</strong> Package 如果每次调用都重新读 catalog、解析包体、构造执行上下文，性能会很差。IvorySQL 的 <code>PackageCache</code> 放在 <code>CacheMemoryContext</code>，通过 syscache callback 管理失效。package 或 package body 发生 DDL 变化后，相关缓存和 plan cache 会被清理；没有变化时，调用可以复用缓存对象。它比临时拼装式实现更适合长期运行的业务系统。</p>
<p><strong>第五，<code>ROWNUM</code> 优化只在能保证等价时发生。</strong> 简单的 <code>ROWNUM &lt;= N</code>、<code>ROWNUM = 1</code>、<code>ROWNUM &lt; N</code> 可以改写成 <code>LIMIT</code>。复杂查询里，如果 Oracle <code>ROWNUM</code> 和 PostgreSQL <code>LIMIT</code> 的生效时机不一致，IvorySQL 就不强行改写。兼容数据库不能为了让某些 SQL 看起来更快，就让复杂场景结果悄悄变错。</p>
<p><strong>第六，<code>sys</code> schema 和扩展对象减少了名字解析干扰。</strong> Oracle 函数、类型、系统视图和内置包主要由 <code>ivorysql_ora</code> 扩展和 <code>sys</code> schema 提供。Oracle 模式下 search_path 优先查 <code>sys</code>，PG 模式下不需要把这些对象放进普通 SQL 的默认查找路径。</p>
<p><strong>第七，PG 和 Oracle 测试目标是分开的。</strong> 项目里可以看到 <code>oracle-check</code>、<code>oracle-installcheck</code>、<code>oracle-pg-check</code> 等测试目标。这说明 IvorySQL 不只是验证 Oracle SQL 能不能跑，也在验证 Oracle 兼容改动有没有影响 PG 侧行为。兼容功能越多，越需要持续证明 PG 模式没有被误伤。</p>
<p>所以，IvorySQL 的性能逻辑可以概括成一句话：Oracle 兼容不是摆在所有 SQL 前面的一层解析器，而是通过模式、parser 指针、hook 条件、schema、扩展、catalog 和 cache 被放在特定路径上。PG 模式下，绝大多数 Oracle 逻辑不会进入热路径；Oracle 模式下，额外成本主要来自用户确实使用了 Oracle 语义。这种成本是功能成本，不是架构上的无差别打击。</p>
<p>这也是为什么 IvorySQL 的设计比"统一拦截所有 SQL 再判断方言"的方案更健康。它让 PG 继续像 PG 一样运行，让 Oracle 兼容在需要的时候生效。对于企业数据库来说，这种边界比单点优化更重要。边界清楚，性能才有讨论空间；边界不清楚，再多微优化也容易被全局分支和语义污染抵消。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="四总结ivorysql-对-pg-生态的业务贡献">四、总结：IvorySQL 对 PG 生态的业务贡献<a href="https://ivorysql.org/zh-cn/blog/oracle-compat-kernel#%E5%9B%9B%E6%80%BB%E7%BB%93ivorysql-%E5%AF%B9-pg-%E7%94%9F%E6%80%81%E7%9A%84%E4%B8%9A%E5%8A%A1%E8%B4%A1%E7%8C%AE" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL 对 PG 生态的贡献，在于给 PostgreSQL 生态加上了一条面向 Oracle 存量系统迁移的内核级路径。</p>
<p>企业系统里，Oracle 往往不是一个单纯的数据存储。大量业务逻辑、权限习惯、报表工具、运维脚本、数据字典查询、存储过程和包都围绕 Oracle 建了很多年。IvorySQL 把 parser、类型、Package、PL/iSQL、系统视图、内置函数、会话语义这些迁移难点放到数据库侧处理，本质上是在降低企业拥抱 PG 生态的门槛。</p>
<p>对业务方来说，这意味着迁移项目没必要一开始就大规模重写。存储过程、包、隐式转换、Oracle 数据字典依赖这些历史包袱，可以先由数据库兼容能力承载一部分。应用团队可以把精力放在真正需要改造的业务逻辑上，而不是把大量时间花在低层语法和对象差异上，这无疑让项目的启动和验证变得更加低成本无负担。</p>
<p>对 PG 生态来说，IvorySQL 的意义就更不言而喻。PostgreSQL 原本就有稳定的内核、成熟的优化器、活跃的扩展生态和开放的社区基础，但在 Oracle 存量市场里，兼容深度一直是企业决策时绕不开的问题。IvorySQL 用内核增强的方式补齐了这块短板，让 PG 生态在金融、电信、政企、传统制造这些 Oracle 深度用户的场景里，多了一个更现实的落地选择。</p>
<p>更难得的是，IvorySQL 没有用牺牲 PG 原生体验来换 Oracle 兼容。它通过模式隔离、双 parser、hook 条件、<code>sys</code> schema、扩展对象和缓存机制，把 Oracle 兼容放在该出现的位置。PG 应用可以继续按 PG 的方式运行，Oracle 迁移应用也能获得更接近原系统的语义。这种路线对生态很友好：它扩大了 PostgreSQL 的适用范围，却没有把 PG 本身改成一个不可辨认的混合体。</p>]]></content:encoded>
            <category>IvorySQL</category>
            <category>Oracle</category>
            <category>PostgreSQL</category>
            <category>Kernel</category>
            <category>PL/iSQL</category>
            <category>Migration</category>
        </item>
        <item>
            <title><![CDATA[IvorySQL HTAP 实时湖仓接入引擎]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/ivorysql-htap</link>
            <guid>https://ivorySQL.org/zh-cn/blog/ivorysql-htap</guid>
            <pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[本文章整理于 HOW 2026 中陶郑（IvorySQL 核心贡献者）演讲内容。]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>本文章整理于 HOW 2026 中陶郑（IvorySQL 核心贡献者）演讲内容。
视频回放：<a href="https://www.bilibili.com/video/BV1KdLq6sEV3/" target="_blank" rel="noopener noreferrer" class="">https://www.bilibili.com/video/BV1KdLq6sEV3/</a></p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="一数据架构的演进历程">一、数据架构的演进历程<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#%E4%B8%80%E6%95%B0%E6%8D%AE%E6%9E%B6%E6%9E%84%E7%9A%84%E6%BC%94%E8%BF%9B%E5%8E%86%E7%A8%8B" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-传统数据架构的局限">1.1 传统数据架构的局限<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#11-%E4%BC%A0%E7%BB%9F%E6%95%B0%E6%8D%AE%E6%9E%B6%E6%9E%84%E7%9A%84%E5%B1%80%E9%99%90" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>业务数据从产生到被分析使用，中间需要经过多个系统的流转。当前行业主要存在两种架构模式：</p>
<ul>
<li class=""><strong>线性架构</strong>：业务数据产生后进入事务数据库，经过 ETL 数据迁徙进入数据湖，再经处理进入数据仓库，最终由 AI 调用数据仓库接口进行分析。这一链路冗长，数据处理环节众多。</li>
<li class=""><strong>变形架构</strong>：数据同时写入事务数据库和分析型数据库（即数据库与数据仓库并存），但本质上仍是多个数据库系统，AI 查询仍需跨系统访问。</li>
</ul>
<p>这两类架构共同面临的困境是：数据处理流程复杂导致分钟级甚至小时级的延迟，且多次查询间原始数据可能已经发生变化，造成数据割裂。与此同时，多套系统的运维成本也居高不下。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-架构演进的三代路径">1.2 架构演进的三代路径<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#12-%E6%9E%B6%E6%9E%84%E6%BC%94%E8%BF%9B%E7%9A%84%E4%B8%89%E4%BB%A3%E8%B7%AF%E5%BE%84" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class=""><strong>第一代（2000年代——数据仓库）</strong>：随着数据量增长，企业开始按业务需求设计数据结构并存入数据仓库。但后期业务扩展时，新增数据分析需求往往面临数据仓库改造的高昂成本。</li>
<li class=""><strong>第二代（2010年代——数据湖）</strong>：为降低成本，企业开始将全量增量数据保存至廉价的数据湖中，仅在需要时提取至数据仓库。这虽降低了存储成本，却使分析链路更加复杂，维护难度加大。</li>
<li class=""><strong>第三代（2020年前后——湖仓一体）</strong>：融合数据仓库与数据湖的优势，但本质上仍是两个系统的组合。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-htap-原生化下一代架构">1.3 HTAP 原生化：下一代架构<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#13-htap-%E5%8E%9F%E7%94%9F%E5%8C%96%E4%B8%8B%E4%B8%80%E4%BB%A3%E6%9E%B6%E6%9E%84" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>当前最新趋势是从"湖仓一体"向"HTAP 原生化"演进——让事务数据与分析数据存储在同一数据库中，彻底消除数据迁徙与 ETL 环节。</p>
<p>两代架构的核心差异：</p>
<table><thead><tr><th>维度</th><th>湖仓一体</th><th>HTAP 原生化</th></tr></thead><tbody><tr><td>存储底座</td><td>对象存储</td><td>统一高性能存储引擎</td></tr><tr><td>数据新鲜度</td><td>分钟级/小时级</td><td>秒级/毫秒级</td></tr><tr><td>核心能力</td><td>数据湖增加数仓功能</td><td>数据库具备数仓体量</td></tr></tbody></table>
<p>这意味着多个数据库整合为一个，AI 可通过单一 API 调用，无需跨库查询，运维成本显著降低，数据实时性大幅提升。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="二ai-时代传统架构的三大痛点">二、AI 时代传统架构的三大痛点<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#%E4%BA%8Cai-%E6%97%B6%E4%BB%A3%E4%BC%A0%E7%BB%9F%E6%9E%B6%E6%9E%84%E7%9A%84%E4%B8%89%E5%A4%A7%E7%97%9B%E7%82%B9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<ul>
<li class=""><strong>痛点一：查询模式不可预测</strong>——Agent 生成的查询是随机的，传统架构无法同时高效应对点查和聚合分析。</li>
<li class=""><strong>痛点二：数据新鲜度敏感</strong>——ETL 延迟意味着 Agent 查询时数据可能已变化，在风控、推荐、金融等场景中不可接受。</li>
<li class=""><strong>痛点三：跨系统延迟叠加</strong>——当单次任务同时涉及点查和聚合分析时，跨系统的延迟相互叠加。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="三htap-原生架构行业动向与-ivorysql-的定位">三、HTAP 原生架构：行业动向与 IvorySQL 的定位<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#%E4%B8%89htap-%E5%8E%9F%E7%94%9F%E6%9E%B6%E6%9E%84%E8%A1%8C%E4%B8%9A%E5%8A%A8%E5%90%91%E4%B8%8E-ivorysql-%E7%9A%84%E5%AE%9A%E4%BD%8D" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-行业风向标databricks-的两笔关键并购">3.1 行业风向标：Databricks 的两笔关键并购<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#31-%E8%A1%8C%E4%B8%9A%E9%A3%8E%E5%90%91%E6%A0%87databricks-%E7%9A%84%E4%B8%A4%E7%AC%94%E5%85%B3%E9%94%AE%E5%B9%B6%E8%B4%AD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class=""><strong>2025年5月——收购 Neon</strong>：无服务器 PostgreSQL 服务提供商，意味着 OLAP 巨头主动进入 OLTP 领域。</li>
<li class=""><strong>2025年10月——收购 Mooncake Labs</strong>：核心项目 pg_mooncake——在 PostgreSQL 内嵌入 DuckDB 列存引擎，使同一份数据同时支持事务查询与分析查询，彻底无需 ETL。IvorySQL 的方案正是基于 pg_mooncake 构建。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-pg_mooncake-留下的两个空白">3.2 pg_mooncake 留下的两个空白<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#32-pg_mooncake-%E7%95%99%E4%B8%8B%E7%9A%84%E4%B8%A4%E4%B8%AA%E7%A9%BA%E7%99%BD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class=""><strong>空白一：开源生态的维护缺口</strong>——被收购后更新频率明显放缓，开源社区需要一个独立维护的实现。</li>
<li class=""><strong>空白二：Oracle 迁移场景的能力缺口</strong>——pg_mooncake 从未考虑过 Oracle 兼容场景，而中国大量企业的核心数据仍运行在 Oracle 上。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-ivorysql-的定位与填补">3.3 IvorySQL 的定位与填补<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#33-ivorysql-%E7%9A%84%E5%AE%9A%E4%BD%8D%E4%B8%8E%E5%A1%AB%E8%A1%A5" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL 是一个开源的 PostgreSQL 分支，由社区独立维护，完整兼容 PostgreSQL 生态，同时支持 Oracle 语法、数据类型和函数行为。基于这一基础，IvorySQL 的 HTAP 引擎做到：</p>
<ul>
<li class="">填补空白一：社区独立维护的 pg_mooncake 持续演进版本，迭代方向不受商业公司绑定</li>
<li class="">填补空白二：全面支持 Oracle 迁移场景的 HTAP 能力</li>
<li class="">实时湖仓接入引擎：同一份数据同时支持事务与分析两条路径，实时可查</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="四技术路径与架构原理">四、技术路径与架构原理<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#%E5%9B%9B%E6%8A%80%E6%9C%AF%E8%B7%AF%E5%BE%84%E4%B8%8E%E6%9E%B6%E6%9E%84%E5%8E%9F%E7%90%86" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-行存与列存理解-htap-的技术基础">4.1 行存与列存：理解 HTAP 的技术基础<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#41-%E8%A1%8C%E5%AD%98%E4%B8%8E%E5%88%97%E5%AD%98%E7%90%86%E8%A7%A3-htap-%E7%9A%84%E6%8A%80%E6%9C%AF%E5%9F%BA%E7%A1%80" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>PostgreSQL 是行存数据库，数据按整行连续存储，适合事务型操作。列存则按列独立排列，查询时可直接提取目标列，无需整行扫描，在大数据量聚合分析场景下性能优势显著。HTAP 的核心思路是：<strong>在同一数据库中同时拥有行存与列存两种存储形态，根据查询特征自动选择最优执行路径。</strong></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-整体架构">4.2 整体架构<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#42-%E6%95%B4%E4%BD%93%E6%9E%B6%E6%9E%84" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL HTAP 引擎基于 pg_mooncake 构建，核心机制如下：</p>
<p><strong>智能路由</strong>：通过 Hooks 方式嵌入 PostgreSQL 内核（不侵入内核代码），Planner 自动识别查询特征，判断走行存（点查/事务）还是列存（聚合/分析）效率更高，自动路由至最优执行路径。</p>
<p><strong>双存储引擎</strong>：行存采用 PG Heap 文件（OLTP），列存采用 DuckDB 引擎 + Parquet 列存格式（OLAP）。</p>
<p><strong>内存共享与自动同步</strong>：在内存中，同一份数据既可通过 PG 行存访问，也可转化为列存供 DuckDB 查询。持久化层面，数据通过 Parquet 等列存格式保存，行存与列存之间自动实时同步，应用层完全无感知。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-为什么不需要-etl">4.3 为什么不需要 ETL<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#43-%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E9%9C%80%E8%A6%81-etl" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>传统方案中，数据库 → ETL → 数仓，延迟达分钟级甚至小时级。HTAP 原生方案中，数据始终只有一份，行存和列存是同一份数据的两种访问路径——ETL 消失了，延迟也消失了。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="44-写入路径与查询路由">4.4 写入路径与查询路由<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#44-%E5%86%99%E5%85%A5%E8%B7%AF%E5%BE%84%E4%B8%8E%E6%9F%A5%E8%AF%A2%E8%B7%AF%E7%94%B1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ol>
<li class="">应用层 SQL：标准 SQL 语句，无需任何改造</li>
<li class="">PostgreSQL 查询层：Parser → Planner → Executor，pg_mooncake 以 Hook 方式嵌入</li>
<li class="">自动路由：Planner 识别查询特征，自动决定行存或列存</li>
<li class="">双存储引擎执行：行存走 PG Heap，列存走 DuckDB + Parquet</li>
<li class="">自动同步：两种存储之间自动同步，数据始终只有一份</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="五应用场景">五、应用场景<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#%E4%BA%94%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="场景一oracle-数仓迁移">场景一：Oracle 数仓迁移<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#%E5%9C%BA%E6%99%AF%E4%B8%80oracle-%E6%95%B0%E4%BB%93%E8%BF%81%E7%A7%BB" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>迁移后，Oracle 类型、语法、函数行为全面兼容，HTAP 能力随迁移一并到位，分析链路不需要重建。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="场景二ai-agent-混合查询">场景二：AI Agent 混合查询<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#%E5%9C%BA%E6%99%AF%E4%BA%8Cai-agent-%E6%B7%B7%E5%90%88%E6%9F%A5%E8%AF%A2" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>点查走行存，聚合走列存，一个连接搞定所有查询。数据实时一致，Agent 响应速度与决策质量同步提升。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="场景三实时风控">场景三：实时风控<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#%E5%9C%BA%E6%99%AF%E4%B8%89%E5%AE%9E%E6%97%B6%E9%A3%8E%E6%8E%A7" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>交易写入的同时，列存即可查询。风控规则实时触发，从分钟级降到秒级以内。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="六开源发布计划">六、开源发布计划<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#%E5%85%AD%E5%BC%80%E6%BA%90%E5%8F%91%E5%B8%83%E8%AE%A1%E5%88%92" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<ul>
<li class=""><strong>发布时间</strong>：计划伴随 IvorySQL 6.0 版本正式发布</li>
<li class=""><strong>开源内容</strong>：代码完全开源，包含三个核心模块——ivy_mooncake、ivy_duckdb、ivy_moonlink</li>
</ul>
<blockquote>
<p>注：现在已经发布了预览版 <a href="https://github.com/IvorySQL/ivy_mooncake/releases/tag/IvorySQL_1.0_beta1" target="_blank" rel="noopener noreferrer" class="">1.0 beta1</a></p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="七总结">七、总结<a href="https://ivorysql.org/zh-cn/blog/ivorysql-htap#%E4%B8%83%E6%80%BB%E7%BB%93" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL HTAP 实时湖仓接入引擎，是 IvorySQL 社区在数据库架构演进方向上的重要布局。项目紧跟 Databricks 等行业巨头验证的技术方向，基于 pg_mooncake 构建，同时填补了开源独立维护与 Oracle 迁移场景两大空白。通过行存与列存的融合、智能路由与自动同步机制，实现了事务与分析在同一数据库中的实时统一，为 AI Agent 时代的数据查询、实时风控、Oracle 迁移等场景提供了切实可行的解决方案。随着 6.0 版本的开源发布，IvorySQL 社区将为企业用户提供一套真正开放、可独立演进、兼容 Oracle 生态的 HTAP 基础设施。</p>]]></content:encoded>
            <category>IvorySQL</category>
            <category>HTAP</category>
            <category>DuckDB</category>
            <category>PostgreSQL</category>
            <category>Lakehouse</category>
            <category>Mooncake</category>
        </item>
        <item>
            <title><![CDATA[IvorySQL 深度解析：融合 PostgreSQL 生态与 Oracle 兼容性的革新之路]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/ivorysql-deepdive</link>
            <guid>https://ivorySQL.org/zh-cn/blog/ivorysql-deepdive</guid>
            <pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[作者：杨宇，PG ACE，第八届 PG 数据库生态大会 2025 年度金牌讲师，PG 分会哈尔滨用户组核心成员，负责本公司数据库、云计算等相关运维工作。曾主持对公司 PostgreSQL 数据库进行架构设计、容灾系统搭建及报表优化工作，发表多篇架构、优化相关的原创文章。目前拥有 ORACLE 11G/12C OCM、MYSQL 5.7/8.0 OCP、TIDB PCTP、OBCP V4、电科金仓 KCP 等资质认证。]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>作者：杨宇，PG ACE，第八届 PG 数据库生态大会 2025 年度金牌讲师，PG 分会哈尔滨用户组核心成员，负责本公司数据库、云计算等相关运维工作。曾主持对公司 PostgreSQL 数据库进行架构设计、容灾系统搭建及报表优化工作，发表多篇架构、优化相关的原创文章。目前拥有 ORACLE 11G/12C OCM、MYSQL 5.7/8.0 OCP、TIDB PCTP、OBCP V4、电科金仓 KCP 等资质认证。</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="引言数据库领域的-最佳桥梁">引言：数据库领域的 "最佳桥梁"<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#%E5%BC%95%E8%A8%80%E6%95%B0%E6%8D%AE%E5%BA%93%E9%A2%86%E5%9F%9F%E7%9A%84-%E6%9C%80%E4%BD%B3%E6%A1%A5%E6%A2%81" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>在当今多元化的数据库技术栈中，企业常常面临一个两难选择：是拥抱开源、生态丰富、性能卓越的 PostgreSQL，还是沿用商业强大、生态成熟但成本高昂的 Oracle 数据库？迁移的复杂性、高昂的许可费用以及对现有应用的兼容性担忧，往往让企业在技术选型和迁移上举步维艰。</p>
<p>IvorySQL 正是在这一背景下应运而生。它并非一个简单的数据库，而是一个旨在架起 PostgreSQL 与 Oracle 之间桥梁的革新性开源项目。它基于 PostgreSQL 开发，在保持与原版数据库 100% 语法兼容的同时，深度兼容 Oracle 语法，为企业提供了一条平滑、低成本的迁移与融合路径。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="核心定位生于-postgresql优于-oracle-兼容">核心定位：生于 PostgreSQL，优于 Oracle 兼容<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#%E6%A0%B8%E5%BF%83%E5%AE%9A%E4%BD%8D%E7%94%9F%E4%BA%8E-postgresql%E4%BC%98%E4%BA%8E-oracle-%E5%85%BC%E5%AE%B9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL 的核心哲学可以概括为：以 PostgreSQL 为基石，以 Oracle 兼容性为核心。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="坚守开源与-postgresql-生态">坚守开源与 PostgreSQL 生态<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#%E5%9D%9A%E5%AE%88%E5%BC%80%E6%BA%90%E4%B8%8E-postgresql-%E7%94%9F%E6%80%81" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL 社区明确承诺，始终与 PostgreSQL 数据库保持 100% 兼容，并可直接替换最新版本的 PostgreSQL。这意味着 PostgreSQL 强大的扩展生态系统（如 PostGIS、pgvector、pg_cron 等）在 IvorySQL 中得以完整保留。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="强大的-oracle-兼容性">强大的 Oracle 兼容性<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#%E5%BC%BA%E5%A4%A7%E7%9A%84-oracle-%E5%85%BC%E5%AE%B9%E6%80%A7" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>这是 IvorySQL 区别于其他基于 PostgreSQL 的分支的核心竞争力。其关键在于 <code>ivorysql.compatible_mode</code> 这个 GUC 参数。通过将其设置为 <code>oracle</code> 或 <code>pg</code>，用户可以在同一套数据库系统内无缝切换 Oracle 兼容模式和原生 PostgreSQL 模式，这在数据库产品中是极为创新的设计。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="深度技术解析如何实现-无缝兼容">深度技术解析：如何实现 "无缝兼容"？<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#%E6%B7%B1%E5%BA%A6%E6%8A%80%E6%9C%AF%E8%A7%A3%E6%9E%90%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0-%E6%97%A0%E7%BC%9D%E5%85%BC%E5%AE%B9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-guc-参数驱动的双模式与初始化">1. GUC 参数驱动的双模式与初始化<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#1-guc-%E5%8F%82%E6%95%B0%E9%A9%B1%E5%8A%A8%E7%9A%84%E5%8F%8C%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%88%9D%E5%A7%8B%E5%8C%96" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="初始化时指定模式">初始化时指定模式<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#%E5%88%9D%E5%A7%8B%E5%8C%96%E6%97%B6%E6%8C%87%E5%AE%9A%E6%A8%A1%E5%BC%8F" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<p><strong>初始化为纯 PostgreSQL 模式</strong>：<code>initdb -D /usr/ivory-5/data -m pg</code>，使用 <code>-m pg</code> 模式后 <code>ivorysql.compatible_mode</code> 参数将失效，系统行为与原生 PostgreSQL 完全一致。</p>
<p><strong>初始化为 Oracle 兼容模式（默认）</strong>：<code>initdb -D /usr/ivory-5/data -m oracle</code></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="运行时动态切换">运行时动态切换<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#%E8%BF%90%E8%A1%8C%E6%97%B6%E5%8A%A8%E6%80%81%E5%88%87%E6%8D%A2" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SHOW</span><span class="token plain"> ivorysql</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">compatible_mode</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 输出: oracle</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SET</span><span class="token plain"> ivorysql</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">compatible_mode </span><span class="token keyword" style="color:#00009f">TO</span><span class="token plain"> pg</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SHOW</span><span class="token plain"> ivorysql</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">compatible_mode</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 输出: pg</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-plisqloracle-plsql-的无缝兼容">2. PL/iSQL：Oracle PL/SQL 的无缝兼容<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#2-plisqloracle-plsql-%E7%9A%84%E6%97%A0%E7%BC%9D%E5%85%BC%E5%AE%B9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p><strong>匿名块兼容</strong></p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">DECLARE</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    i </span><span class="token keyword" style="color:#00009f">integer</span><span class="token plain"> :</span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    grade </span><span class="token keyword" style="color:#00009f">CHAR</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> :</span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'B'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">BEGIN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">CASE</span><span class="token plain"> grade</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">WHEN</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'A'</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">THEN</span><span class="token plain"> RAISE NOTICE </span><span class="token string" style="color:#e3116c">'Excellent'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">WHEN</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'B'</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">THEN</span><span class="token plain"> RAISE NOTICE </span><span class="token string" style="color:#e3116c">'Very Good'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">END</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">CASE</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EXCEPTION</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">WHEN</span><span class="token plain"> CASE_NOT_FOUND </span><span class="token keyword" style="color:#00009f">THEN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        RAISE NOTICE </span><span class="token string" style="color:#e3116c">'No such grade'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">END</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">/</span><br></span></code></pre></div></div>
<p><strong>函数兼容</strong></p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">OR</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">REPLACE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FUNCTION</span><span class="token plain"> add_numbers</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p_a NUMBER</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> p_b NUMBER</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">RETURN</span><span class="token plain"> NUMBER </span><span class="token operator" style="color:#393A34">IS</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">BEGIN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">RETURN</span><span class="token plain"> p_a </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> p_b</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">END</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">/</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> add_numbers</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> dual</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p><strong>包（PACKAGE）兼容</strong></p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">OR</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">REPLACE</span><span class="token plain"> PACKAGE my_pkg </span><span class="token operator" style="color:#393A34">IS</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    var1 </span><span class="token keyword" style="color:#00009f">INTEGER</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">FUNCTION</span><span class="token plain"> get_double</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p_id </span><span class="token keyword" style="color:#00009f">INTEGER</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">RETURN</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTEGER</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">PROCEDURE</span><span class="token plain"> print_message</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p_msg VARCHAR2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">END</span><span class="token plain"> my_pkg</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">/</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">OR</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">REPLACE</span><span class="token plain"> PACKAGE BODY my_pkg </span><span class="token operator" style="color:#393A34">IS</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">FUNCTION</span><span class="token plain"> get_double</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p_id </span><span class="token keyword" style="color:#00009f">INTEGER</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">RETURN</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTEGER</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">IS</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">BEGIN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">RETURN</span><span class="token plain"> p_id </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">END</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">PROCEDURE</span><span class="token plain"> print_message</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p_msg VARCHAR2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">IS</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">BEGIN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        DBMS_OUTPUT</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">PUT_LINE</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p_msg</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">END</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">BEGIN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    var1 :</span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">END</span><span class="token plain"> my_pkg</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">/</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-数据类型与函数">3. 数据类型与函数<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#3-%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E4%B8%8E%E5%87%BD%E6%95%B0" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>IvorySQL 支持大量 Oracle 独有的数据类型和函数：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> customers </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    cust_id NUMBER </span><span class="token keyword" style="color:#00009f">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    cust_name VARCHAR2</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">-- 非标准PG类型，IvorySQL兼容</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p><strong>常用内置函数</strong>：SYSDATE、ADD_MONTHS、NVL、DECODE、INSTRB、SUBSTRB 等。</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> SYSDATE </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> dual</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> NVL</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Default Value'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> dual</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> DECODE</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'One'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Two'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Other'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> dual</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p><strong>RowID 伪列</strong>：<code>SET ivorysql.default_with_rowids TO on;</code></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-其他关键特性">4. 其他关键特性<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#4-%E5%85%B6%E4%BB%96%E5%85%B3%E9%94%AE%E7%89%B9%E6%80%A7" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>支持 Sequence 序列、不可见列（INVISIBLE COLUMN）、%TYPE 和 %ROWTYPE 属性、CALL INTO 子句等。通过 <code>identifier_case_switch</code> 参数解决 Oracle 与 PG 标识符大小写差异问题，提供 normal、interchange、lowercase 三种模式。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="安装部署从单机到云原生的全景图">安装部署：从单机到云原生的全景图<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#%E5%AE%89%E8%A3%85%E9%83%A8%E7%BD%B2%E4%BB%8E%E5%8D%95%E6%9C%BA%E5%88%B0%E4%BA%91%E5%8E%9F%E7%94%9F%E7%9A%84%E5%85%A8%E6%99%AF%E5%9B%BE" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="yum-一键安装">Yum 一键安装<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#yum-%E4%B8%80%E9%94%AE%E5%AE%89%E8%A3%85" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">sudo tee /etc/yum.repos.d/ivorysql.repo &lt;&lt;EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[ivorysql5]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">name=IvorySQL Server 5</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">baseurl=https://yum.highgo.com/dists/ivorysql-rpms/5/redhat/rhel-\$releasever-\$basearch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">enabled=1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">gpgcheck=0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">sudo dnf install -y ivorysql5-5.3</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="docker-部署">Docker 部署<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#docker-%E9%83%A8%E7%BD%B2" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">docker pull ivorysql/ivorysql:5.3-ubi8</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">docker run --name ivorysql -p 5432:5432 -p 1521:1521 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -e IVORYSQL_PASSWORD=mysecretpassword -d ivorysql/ivorysql:5.3-ubi8</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="k8sharbor-operator-云原生部署">K8s/Harbor Operator 云原生部署<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#k8sharbor-operator-%E4%BA%91%E5%8E%9F%E7%94%9F%E9%83%A8%E7%BD%B2" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ivory</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">operator.ivorysql.org/v1beta1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> IvoryCluster</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> hippo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ivorysql/ivorysql</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">5.3</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">ubi8</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">instances</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> instance1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">dataVolumeClaimSpec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">accessModes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"ReadWriteOnce"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">resources</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">requests</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">storage</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 1Gi</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="运维生态让管理更简单让性能更卓越">运维生态：让管理更简单，让性能更卓越<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#%E8%BF%90%E7%BB%B4%E7%94%9F%E6%80%81%E8%AE%A9%E7%AE%A1%E7%90%86%E6%9B%B4%E7%AE%80%E5%8D%95%E8%AE%A9%E6%80%A7%E8%83%BD%E6%9B%B4%E5%8D%93%E8%B6%8A" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>高可用集群基于流复制搭建主备集群。结合 pgMonitor + Grafana 搭建监控大屏。提供常用诊断 SQL：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 查看执行计划</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">EXPLAIN</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">ANALYZE</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> BUFFERS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> large_table </span><span class="token keyword" style="color:#00009f">WHERE</span><span class="token plain"> id </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 查看当前活跃会话</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> pid</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> state</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> query</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> wait_event_type </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> pg_stat_activity </span><span class="token keyword" style="color:#00009f">WHERE</span><span class="token plain"> state </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'active'</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>使用 <code>pg_dump</code> 完成同架构数据迁移。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="结论面向未来的数据基础设施">结论：面向未来的数据基础设施<a href="https://ivorysql.org/zh-cn/blog/ivorysql-deepdive#%E7%BB%93%E8%AE%BA%E9%9D%A2%E5%90%91%E6%9C%AA%E6%9D%A5%E7%9A%84%E6%95%B0%E6%8D%AE%E5%9F%BA%E7%A1%80%E8%AE%BE%E6%96%BD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL 不是一个简单的 PostgreSQL 再发行版，而是一个思考深、设计巧、价值高的开源数据库项目。它成功解决了 Oracle 用户向开源和云原生生态迁移的核心障碍——兼容性。它将 Oracle 的强大与 PostgreSQL 的灵活结合在了一起，为用户在未来的数据结构、混合多模态数据处理上提供了无限的想象空间。</p>
<p>IvorySQL 的出现，标志着数据库领域 "融合" 趋势的进一步发展。它不是去颠覆谁，而是在做一件更有意义的事：让历史与未来，在一个高效、可靠的平台上共存共生。</p>]]></content:encoded>
            <category>IvorySQL</category>
            <category>PostgreSQL</category>
            <category>Oracle</category>
            <category>Migration</category>
            <category>PL/iSQL</category>
        </item>
        <item>
            <title><![CDATA[等了20分钟镜像没拉完？国内源30秒搞定IvorySQL+避坑指南]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/ivorysql-mirror-troubleshooting</link>
            <guid>https://ivorySQL.org/zh-cn/blog/ivorysql-mirror-troubleshooting</guid>
            <pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[先问一个问题：你有没有因为拉一个 Docker 镜像等到怀疑人生？]]></description>
            <content:encoded><![CDATA[<p>先问一个问题：你有没有因为拉一个 Docker 镜像等到怀疑人生？</p>
<p>我有。</p>
<p>去年底想在一台测试机上跑 IvorySQL 5.0，执行 <code>docker pull ivorysql/ivorysql:5.0-ubi8</code>，然后就去泡了杯咖啡。咖啡喝完，进度条走了 30%。又刷了会儿手机，还是 50%。最后干脆去开了个会，回来总算拉完了——前前后后快二十分钟。</p>
<p>你说这算技术问题吗？不算。但这就是劝退很多人的"第一公里"。</p>
<p>所以当我听说 IvorySQL 社区在 <code>registry.highgo.com</code> 上线了国内镜像源，第一反应是：<strong>终于来了。</strong></p>
<p>这篇文章我不会跟你念产品文档。咱们就像平时在技术群里闲聊那样，聊聊这个镜像源到底好不好用、怎么用、以及我踩到的那些坑。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="一镜像仓库地址">一、镜像仓库地址<a href="https://ivorysql.org/zh-cn/blog/ivorysql-mirror-troubleshooting#%E4%B8%80%E9%95%9C%E5%83%8F%E4%BB%93%E5%BA%93%E5%9C%B0%E5%9D%80" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>先放地址：<a href="https://registry.highgo.com/" target="_blank" rel="noopener noreferrer" class="">https://registry.highgo.com/</a></p>
<p>这个镜像服务面向国内网络环境做了优化，适合开发测试、演示验证、CI 构建以及容器化部署场景。说白了就是——在国内拉镜像，不用再绕道 Docker Hub 了。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="二快速体验-ivorysql-镜像">二、快速体验 IvorySQL 镜像<a href="https://ivorysql.org/zh-cn/blog/ivorysql-mirror-troubleshooting#%E4%BA%8C%E5%BF%AB%E9%80%9F%E4%BD%93%E9%AA%8C-ivorysql-%E9%95%9C%E5%83%8F" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-拉取镜像">2.1 拉取镜像<a href="https://ivorysql.org/zh-cn/blog/ivorysql-mirror-troubleshooting#21-%E6%8B%89%E5%8F%96%E9%95%9C%E5%83%8F" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>如果你之前看过 IvorySQL 的 Docker 安装文档，操作方式几乎一样，只需要把镜像地址替换成国内的就行。</p>
<p>以 IvorySQL 5.3 的 UBI8 镜像为例：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">docker pull registry.highgo.com/ivorysql/ivorysql:5.3-ubi8</span><br></span></code></pre></div></div>
<p>我在 openEuler 服务器上实际跑了一下，效果是这样的：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker pull registry.highgo.com/ivorysql/ivorysql:5.3-ubi8</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">5.3-ubi8: Pulling from ivorysql/ivorysql</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">4fba3effa6b3: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">4cc358cba85f: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">c4453bb8971d: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">d396d38d02c2: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">4406b6bc22b7: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">52bcb2e0d3b6: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">bc3a2a4b7a5d: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">addd7eda355c: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">c566c56fc71d: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">5354ee8271dc: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">6adc3134ae3c: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">4f4fb700ef54: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Digest: sha256:c877f3d50eb7d5edb96da5d1f939890ba6f9b031b83faee893403b8de0622c82</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Status: Downloaded newer image for registry.highgo.com/ivorysql/ivorysql:5.3-ubi8</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">registry.highgo.com/ivorysql/ivorysql:5.3-ubi8</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p><strong>实测效果</strong>：我本地用 Docker Hub 拉取同一个镜像，平均速度几百 KB/s，有时候还会断连重试。换成国内镜像源之后，速度直接飙到几十 MB/s。从"泡杯咖啡等它拉完"变成"刚起身水还没倒好就结束了"。</p>
<p>拉完之后校验一下：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">docker images | grep ivorysql</span><br></span></code></pre></div></div>
<p>我在服务器上执行的结果：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker images | grep ivorysql</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">registry.highgo.com/ivorysql/ivorysql               5.3-ubi8   2af458997451   3 months ago   417MB</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p>能看到 IMAGE ID 和 SIZE，说明镜像下载完整、没有损坏。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-启动容器附带踩坑实录">2.2 启动容器（附带踩坑实录）<a href="https://ivorysql.org/zh-cn/blog/ivorysql-mirror-troubleshooting#22-%E5%90%AF%E5%8A%A8%E5%AE%B9%E5%99%A8%E9%99%84%E5%B8%A6%E8%B8%A9%E5%9D%91%E5%AE%9E%E5%BD%95" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>拉取完成后，用下面的命令启动 IvorySQL 容器：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">docker run --name ivorysql -p 5432:5432 -p 1521:1521 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -e IVORYSQL_PASSWORD=PKsjj@112!! -d \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  registry.highgo.com/ivorysql/ivorysql:5.3-ubi8</span><br></span></code></pre></div></div>
<blockquote>
<p>⚠️ <strong>第一个坑：命令被"截断"了</strong></p>
</blockquote>
<p>我第一次执行的时候，因为多行复制时不小心混入了别的命令，结果报了这个错：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker run --name ivorysql -p 5432:5432 -p 1521:1521 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;   -e IVORYSQL_PASSWORD=PKsjj@112!! -d \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  -e IVORYSQL_PASSWORD=PKsjj@112docker images | grep ivorysql -d \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;   registry.highgo.com/ivorysql/ivorysql:5.3-ubi8</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">grep: '--directories' 的参数 'registry.highgo.com/ivorysql/ivorysql:5.3-ubi8' 无效</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">有效参数是:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - 'read'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - 'recurse'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - 'skip'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">用法: grep [选项]... 模式 [文件]...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">尝试使用 'grep --help' 来获得更多信息。</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Unable to find image 'images:latest' locally</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">^C</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p>仔细一看，原来是 <code>-e IVORYSQL_PASSWORD=PKsjj@112docker ps</code> 这一串出了问题——Docker 把 <code>PKsjj@112docker ps</code> 当成了完整的密码值，后面的 <code>-d</code> 被当成了镜像名，然后它跑去 Docker Hub 找了一个叫 <code>ps</code> 的镜像，当然找不到。</p>
<p><strong>解决办法</strong>：要么用单行命令，要么用 <code>\</code> 换行时确保每行末尾没有多余空格。最简单的是直接单行执行：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">docker run --name ivorysql -p 5432:5432 -p 1521:1521 -e IVORYSQL_PASSWORD=PKsjj@112!! -d registry.highgo.com/ivorysql/ivorysql:5.3-ubi8</span><br></span></code></pre></div></div>
<p>但换了单行之后，又出幺蛾子了：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker run --name ivorysql -p 5432:5432 -p 1521:1521 -e IVORYSQL_PASSWORD=PKsjj@112!! -d registry.highgo.com/ivorysql/ivorysql:5.3-ubi8</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">docker run --name ivorysql -p 5432:5432 -p 1521:1521 -e IVORYSQL_PASSWORD=PKsjj@112docker ps -d registry.highgo.com/ivorysql/ivorysql:5.3-ubi8</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Unable to find image 'ps:latest' locally</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">docker: Error response from daemon: Get "https://registry-1.docker.io/v2/": dial tcp 103.39.76.66:443: connect: connection timed out.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">See 'docker run --help'.</span><br></span></code></pre></div></div>
<p>问题还是同一个——<code>-e IVORYSQL_PASSWORD=PKsjj@112docker ps</code> 这串命令里，密码和参数之间没有空格，Docker 直接懵了。</p>
<p><strong>最终正确姿势</strong>：确保密码和 <code>-d</code> 之间有一个明确的空格分隔。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">docker run --name ivorysql -p 5432:5432 -p 1521:1521 -e IVORYSQL_PASSWORD=g4Bw#QPY4^t%87xTy -d registry.highgo.com/ivorysql/ivorysql:5.3-ubi8</span><br></span></code></pre></div></div>
<p>这次终于成功了：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker run --name ivorysql -p 5432:5432 -p 1521:1521 -e IVORYSQL_PASSWORD=g4Bw#QPY4^t%87xTy -d registry.highgo.com/ivorysql/ivorysql:5.3-ubi8</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">5856d79e1f85a51945e31e68ae21161d2e6dc2806697fffa9e0485e02df32f48</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p>用 <code>docker ps</code> 确认一下容器状态：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker ps</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">CONTAINER ID   IMAGE                                            COMMAND                  CREATED              STATUS              PORTS                                                                                            NAMES</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">5856d79e1f85   registry.highgo.com/ivorysql/ivorysql:5.3-ubi8   "docker-entrypoint.s…"   About a minute ago   Up About a minute   0.0.0.0:1521-&gt;1521/tcp, :::1521-&gt;1521/tcp, 0.0.0.0:5432-&gt;5432/tcp, :::5432-&gt;5432/tcp, 5866/tcp   ivorysql</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<blockquote>
<p>💡 <strong>一点小建议</strong>：这种因为复制粘贴、不小心多按了几个键导致的"乌龙"报错，在运维里太常见了。以后遇到 <code>Unable to find image 'xxx:latest' locally</code> 这种报错，第一反应先检查命令里是不是多了什么奇怪的字符，往往比排查网络、镜像源要快得多。</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-双端口的意义">2.3 双端口的意义<a href="https://ivorysql.org/zh-cn/blog/ivorysql-mirror-troubleshooting#23-%E5%8F%8C%E7%AB%AF%E5%8F%A3%E7%9A%84%E6%84%8F%E4%B9%89" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>这里有两个端口需要留意一下：</p>
<ul>
<li class=""><strong>5432 端口</strong>：兼容 PostgreSQL 原生语法</li>
<li class=""><strong>1521 端口</strong>：对标 Oracle 访问端口，适配 Oracle 语法</li>
</ul>
<p>为什么要同时暴露两个端口？因为 IvorySQL 的核心定位就是 <strong>Oracle 兼容</strong>。你在 1521 端口上连接，可以体验到 IvorySQL 针对 Oracle 语法做的兼容适配；在 5432 端口上连接，又像在用标准 PostgreSQL。同一个容器，两套语法，方便你在迁移评估阶段做对比测试。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="24-连接数据库的波折">2.4 连接数据库的波折<a href="https://ivorysql.org/zh-cn/blog/ivorysql-mirror-troubleshooting#24-%E8%BF%9E%E6%8E%A5%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E6%B3%A2%E6%8A%98" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>容器启动后，我想通过 1521 端口连接数据库：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">psql -d ivorysql -U ivorysql -h 127.0.0.1 -p 1521</span><br></span></code></pre></div></div>
<p>结果：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# psql -d ivorysql -U ivorysql -h 127.0.0.1 -p 1521</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">-bash: psql：未找到命令</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<blockquote>
<p>⚠️ <strong>第二个坑：宿主机没有 psql 客户端</strong></p>
</blockquote>
<p><code>psql</code> 是 PostgreSQL 自带的命令行客户端工具，不会随 Docker 自动安装到宿主机上。在 openEuler 系统里直接敲 <code>psql</code>，系统当然找不到。</p>
<p><strong>解决方法</strong>：直接用 <code>docker exec</code> 进入容器内部执行：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">docker exec -it ivorysql psql -d ivorysql -U ivorysql</span><br></span></code></pre></div></div>
<p>结果又报错了：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker exec -it ivorysql psql -d ivorysql -U ivorysql</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: FATAL:  the database system is not yet accepting connections</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">DETAIL:  Consistent recovery state has not been yet reached.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="25-排查过程日志暴露真凶">2.5 排查过程：日志暴露真凶<a href="https://ivorysql.org/zh-cn/blog/ivorysql-mirror-troubleshooting#25-%E6%8E%92%E6%9F%A5%E8%BF%87%E7%A8%8B%E6%97%A5%E5%BF%97%E6%9A%B4%E9%9C%B2%E7%9C%9F%E5%87%B6" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>容器状态显示 <code>Up</code> 只代表容器进程跑起来了，不代表数据库内核加载完毕。这时候需要看日志：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">docker logs ivorysql --tail 50</span><br></span></code></pre></div></div>
<p>我看到的日志是这样的：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker logs ivorysql --tail 50</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:06:57.008 UTC [1] LOG:  checkpointer process (PID 4442) was terminated by signal 6: Aborted</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:06:57.008 UTC [1] LOG:  terminating any other active server processes</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:06:57.009 UTC [1] LOG:  all server processes terminated; reinitializing</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:06:57.043 UTC [4447] LOG:  database system shutdown was interrupted; last known up at 2026-06-17 09:06:56 UTC</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:06:57.067 UTC [4447] LOG:  database system was not properly shut down; automatic recovery in progress</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:06:57.068 UTC [4447] LOG:  redo starts at 0/1ED9BC0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:06:57.068 UTC [4447] LOG:  invalid record length at 0/1ED9BF8: expected at least 24, got 0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:06:57.068 UTC [4447] LOG:  redo done at 0/1ED9BC0 system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:06:57.071 UTC [4448] LOG:  checkpoint starting: end-of-recovery immediate wait</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:06:57.071 UTC [4448] PANIC:  could not write to file "pg_logical/replorigin_checkpoint.tmp": No space left on device</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...（省略部分重复日志）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:06:58.752 UTC [4472] PANIC:  could not write to file "pg_logical/replorigin_checkpoint.tmp": No space left on device</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<blockquote>
<p>⚠️ <strong>第三个坑：磁盘空间满了</strong></p>
</blockquote>
<p>关键报错是 <code>No space left on device</code>。数据库启动时需要写临时文件做 checkpoint，但磁盘已经满了。</p>
<p>用 <code>df -Th</code> 确认一下：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# df -Th</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">文件系统                                     类型      容量  已用  可用 已用% 挂载点</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">devtmpfs                                     devtmpfs  4.0M     0  4.0M    0% /dev</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tmpfs                                        tmpfs      16G  8.0K   16G    1% /dev/shm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tmpfs                                        tmpfs     6.2G  636M  5.6G   11% /run</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tmpfs                                        tmpfs     4.0M     0  4.0M    0% /sys/fs/cgroup</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/dev/mapper/openeuler_openeuler--server-root ext4       41G   39G     0  100% /</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tmpfs                                        tmpfs      16G  4.0K   16G    1% /tmp</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/dev/sda2                                    ext4      920M  312M  545M   37% /boot</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">overlay                                      overlay    41G   39G     0  100% /var/lib/docker/overlay2/cbcb4ac63b0ff3f5953c8cda779af0d2b530eae3aeeebc76ebb09e0c3b36083a/merged</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p><strong>根分区 <code>/</code> 可用空间为 0，已用 100%</strong>。这就是 IvorySQL 容器反复崩溃的根源。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="26-解决方案迁移-docker-数据目录">2.6 解决方案：迁移 Docker 数据目录<a href="https://ivorysql.org/zh-cn/blog/ivorysql-mirror-troubleshooting#26-%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%E8%BF%81%E7%A7%BB-docker-%E6%95%B0%E6%8D%AE%E7%9B%AE%E5%BD%95" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>好消息是服务器上还有一块 100G 的空闲磁盘 <code>/dev/sdb1</code>。我把 Docker 的数据目录整个迁过去：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 停止 Docker 服务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">systemctl stop docker</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 把 Docker 数据目录迁移到新盘</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">mv /var/lib/docker /data/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 创建软链接指向新位置</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ln -s /data/docker /var/lib/docker</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 重启 Docker</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">systemctl start docker</span><br></span></code></pre></div></div>
<p>实际执行记录：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# systemctl stop docker</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# mv /var/lib/docker /data/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# ln -s /data/docker /var/lib/docker</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# systemctl start docker</span><br></span></code></pre></div></div>
<p>迁移完成后，磁盘空间正常了：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# df -Th</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">文件系统                                     类型      容量  已用  可用 已用% 挂载点</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">devtmpfs                                     devtmpfs  4.0M     0  4.0M    0% /dev</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tmpfs                                        tmpfs      16G  8.0K   16G    1% /dev/shm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tmpfs                                        tmpfs     6.2G  660M  5.6G   11% /run</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tmpfs                                        tmpfs     4.0M     0  4.0M    0% /sys/fs/cgroup</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/dev/mapper/openeuler_openeuler--server-root ext4       41G   36G  2.9G   93% /</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tmpfs                                        tmpfs      16G  4.0K   16G    1% /tmp</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/dev/sda2                                    ext4      920M  312M  545M   37% /boot</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/dev/sdb1                                    ext4       98G  7.0G   86G    8% /data</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="27-重新部署终于成功">2.7 重新部署，终于成功<a href="https://ivorysql.org/zh-cn/blog/ivorysql-mirror-troubleshooting#27-%E9%87%8D%E6%96%B0%E9%83%A8%E7%BD%B2%E7%BB%88%E4%BA%8E%E6%88%90%E5%8A%9F" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>删除旧容器，重新启动：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker rm -f ivorysql</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ivorysql</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker run --name ivorysql -p 5432:5432 -p 1521:1521 \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;   -e IVORYSQL_PASSWORD=g4Bw#QPY4^t%87xTy -d \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;   registry.highgo.com/ivorysql/ivorysql:5.3-ubi8</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">90f5945502a072af760a738d2f962c73bcaadd91351fcbe0a5e80a03d14fceb5</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p>等几秒后查看日志，这次终于看到 <code>ready to accept connections</code> 了：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker logs ivorysql --tail 20</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:38:35.117 UTC [51] LOG:  shutting down</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:38:35.118 UTC [51] LOG:  checkpoint starting: shutdown immediate</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:38:35.125 UTC [51] LOG:  checkpoint complete: wrote 0 buffers (0.0%), wrote 3 SLRU buffers; 0 WAL file(s) added, 0 removed, 0 recycled; write=0.004 s, sync=0.001 s, total=0.009 s; sync files=2, longest=0.001 s, average=0.001 s; distance=0 kB, estimate=0 kB; lsn=0/1ED9B48, redo lsn=0/1ED9B48</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:38:35.141 UTC [47] LOG:  database system is shut down</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> done</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">server stopped</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">PostgreSQL init process complete; ready for start up.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:38:35.258 UTC [1] LOG:  starting PostgreSQL 18.3 (IvorySQL 5.3) on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-28), 64-bit</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:38:35.258 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:38:35.258 UTC [1] LOG:  listening on IPv6 address "::", port 5432</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:38:35.259 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 1521</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:38:35.259 UTC [1] LOG:  listening on IPv6 address "::", port 1521</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:38:35.260 UTC [1] LOG:  listening on Unix socket "/tmp/.s.PGSQL.5432"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:38:35.262 UTC [1] LOG:  listening on Unix socket "/tmp/.s.PGSQL.1521"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:38:35.270 UTC [67] LOG:  database system was shut down at 2026-06-17 09:38:35 UTC</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:38:35.276 UTC [1] LOG:  database system is ready to accept connections</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:43:35.369 UTC [65] LOG:  checkpoint starting: time</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2026-06-17 09:43:40.796 UTC [65] LOG:  checkpoint complete: wrote 54 buffers (0.3%), wrote 3 SLRU buffers; 0 WAL file(s) added, 0 removed, 0 recycled; write=5.419 s, sync=0.003 s, total=5.428 s; sync files=11, longest=0.002 s, average=0.001 s; distance=432 kB, estimate=432 kB; lsn=0/1F45EF0, redo lsn=0/1F45E98</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p><code>docker ps</code> 确认容器正常运行：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker ps</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">CONTAINER ID   IMAGE                                            COMMAND                  CREATED          STATUS          PORTS                                                                                            NAMES</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">90f5945502a0   registry.highgo.com/ivorysql/ivorysql:5.3-ubi8   "docker-entrypoint.s…"   12 minutes ago   Up 12 minutes   0.0.0.0:1521-&gt;1521/tcp, :::1521-&gt;1521/tcp, 0.0.0.0:5432-&gt;5432/tcp, :::5432-&gt;5432/tcp, 5866/tcp   ivorysql</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p>再次测试连接：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker exec -it ivorysql psql -d ivorysql -U ivorysql</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">psql (18.3)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Type "help" for help.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ivorysql=#</span><br></span></code></pre></div></div>
<p>查看版本确认：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ivorysql</span><span class="token operator" style="color:#393A34">=</span><span class="token comment" style="color:#999988;font-style:italic"># select version();</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                                        version                          </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                               </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-----------------------------------------------------------------------------------------</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-------------------------------</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> PostgreSQL </span><span class="token number" style="color:#36acaa">18.3</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">IvorySQL </span><span class="token number" style="color:#36acaa">5.3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">on</span><span class="token plain"> x86_64</span><span class="token operator" style="color:#393A34">-</span><span class="token plain">pc</span><span class="token operator" style="color:#393A34">-</span><span class="token plain">linux</span><span class="token operator" style="color:#393A34">-</span><span class="token plain">gnu</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> compiled </span><span class="token keyword" style="color:#00009f">by</span><span class="token plain"> gcc </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">GCC</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8.5</span><span class="token number" style="color:#36acaa">.0</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20210</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">514</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Red Hat </span><span class="token number" style="color:#36acaa">8.5</span><span class="token number" style="color:#36acaa">.0</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">28</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">64</span><span class="token operator" style="color:#393A34">-</span><span class="token keyword" style="color:#00009f">bit</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">row</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ivorysql</span><span class="token operator" style="color:#393A34">=</span><span class="token comment" style="color:#999988;font-style:italic">#</span><br></span></code></pre></div></div>
<blockquote>
<p>💡 <strong>小提示</strong>：如果你用 Navicat 或 DBeaver 等外部工具连接，也需要等初始化完成。刚启动立刻连会报连接失败，建议等待 1-2 分钟再测试。</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="三postgresql-镜像也能拉">三、PostgreSQL 镜像也能拉<a href="https://ivorysql.org/zh-cn/blog/ivorysql-mirror-troubleshooting#%E4%B8%89postgresql-%E9%95%9C%E5%83%8F%E4%B9%9F%E8%83%BD%E6%8B%89" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>除了 IvorySQL，这个镜像仓库里也同步了 PostgreSQL 官方镜像。如果你需要在原生 PG 和 IvorySQL 之间做对比测试或者迁移评估，可以统一从这里拉：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">docker pull registry.highgo.com/postgres/postgres:18.3-bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">docker pull registry.highgo.com/postgres/postgres:18.3-trixie</span><br></span></code></pre></div></div>
<p>我在服务器上实测拉取 <code>18.3-bookworm</code>：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker pull registry.highgo.com/postgres/postgres:18.3-bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">18.3-bookworm: Pulling from postgres/postgres</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">67cf8384fcca: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">047252d203fc: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">285c515ff12e: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">c04bac481cf9: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">4d2f8983bdeb: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">928cbc530e9b: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">b7d7c0b1553c: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ef11daf91965: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">5dab6177213d: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">b73f5647695f: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">9c1186127d05: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">c73bb36f2399: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">142af709c9ab: Pull complete </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Digest: sha256:1fa4bccc8246ee2ba9592895d804dcf767119e632999ef22d2b3355a93b627c3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Status: Downloaded newer image for registry.highgo.com/postgres/postgres:18.3-bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">registry.highgo.com/postgres/postgres:18.3-bookworm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]#</span><br></span></code></pre></div></div>
<p>目前可用的主要是带发行版后缀的标签，比如 <code>18.3-bookworm</code>、<code>18.3-trixie</code> 这些。</p>
<blockquote>
<p>⚠️ <strong>第四个坑：别凭感觉猜版本号</strong></p>
</blockquote>
<p>我一开始想当然地以为能拉 <code>postgres:18.4</code> 或者 <code>postgres:15.18</code> 这种纯数字标签，结果：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker pull registry.highgo.com/postgres/postgres:18.4</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Error response from daemon: unknown: artifact postgres/postgres:18.4 not found</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[root@openeuler-server ~]# docker pull registry.highgo.com/postgres/postgres:15.18</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Error response from daemon: unknown: artifact postgres/postgres:15.18 not found</span><br></span></code></pre></div></div>
<p>所以拉之前最好去 <code>registry.highgo.com</code> 网页上确认一下有哪些 tag 可用，别凭感觉猜。</p>
<p>另外顺便提一句，如果你执行 <code>docker search postgres</code> 报错，那是 Docker 客户端默认去 Docker Hub 搜索被墙了，跟国内镜像仓库没关系。国内仓库本身不提供 <code>docker search</code> 功能。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="四不止是数据库内核">四、不止是数据库内核<a href="https://ivorysql.org/zh-cn/blog/ivorysql-mirror-troubleshooting#%E5%9B%9B%E4%B8%8D%E6%AD%A2%E6%98%AF%E6%95%B0%E6%8D%AE%E5%BA%93%E5%86%85%E6%A0%B8" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL 的国内镜像仓库不只是提供了数据库内核镜像。围绕容器化部署场景，社区还在持续完善更多生态镜像：</p>
<table><thead><tr><th>类别</th><th>镜像</th></tr></thead><tbody><tr><td><strong>高可用部署</strong></td><td><code>ivorysql/docker-compose-ha-cluster</code>、<code>ivorysql/k8s-ha-cluster</code></td></tr><tr><td><strong>Kubernetes Operator</strong></td><td><code>ivorysql/ivory-operator</code></td></tr><tr><td><strong>空间数据扩展</strong></td><td><code>ivorysql/postgis</code></td></tr><tr><td><strong>管理工具</strong></td><td><code>ivorysql/pgadmin</code></td></tr><tr><td><strong>备份恢复</strong></td><td><code>ivorysql/pgbackrest</code></td></tr><tr><td><strong>连接池</strong></td><td><code>ivorysql/pgbouncer</code></td></tr><tr><td><strong>监控</strong></td><td><code>ivorysql/postgres-exporter</code></td></tr></tbody></table>
<p>这意味着你可以用一套镜像源，完成从"单机体验"到"生产级部署"的全链路支撑。不用在 Docker Hub、GitHub、各个官网之间来回跳转找资源。</p>
<p>如果你在实际使用中有其他镜像需求，也可以反馈给 IvorySQL 社区。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="五一点个人看法">五、一点个人看法<a href="https://ivorysql.org/zh-cn/blog/ivorysql-mirror-troubleshooting#%E4%BA%94%E4%B8%80%E7%82%B9%E4%B8%AA%E4%BA%BA%E7%9C%8B%E6%B3%95" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>镜像源上线这件事，单独看就是个基础设施优化。但放在 IvorySQL 的发展路径上来看，信号不太一样。</p>
<p>IvorySQL 5.3 是基于 PostgreSQL 18.3 构建的，2026 年 3 月 12 日正式发布。从版本迭代节奏来看，社区在同步 PG 最新内核方面跟得挺紧。而国内镜像源的上线，补上的是"用户触达"这一环——让想试的人能真正试起来，而不是卡在第一公里的网络问题上。</p>
<p>但镜像源只是起点。后续值得关注的点至少还有两个：</p>
<p><strong>第一，版本同步的时效性。</strong> 镜像源能不能做到跟 Docker Hub 几乎同步更新？尤其像 PG 18.3 这种小版本修复，如果在镜像源里滞后几天，对于依赖最新安全补丁的用户来说就是隐患。</p>
<p><strong>第二，国产操作系统的原生支持。</strong> 目前镜像基于 UBI8，在 x86 和 ARM 上跑都没问题。但如果在麒麟、统信这些国产 OS 上跑，会不会有依赖兼容性问题？这是很多政企用户真正关心的。</p>
<p>毕竟，技术文章最大的价值不是告诉别人"这么做是对的"，而是告诉别人"别像我那样做"。</p>
<blockquote>
<p><strong>作者：</strong> ShunWah<br>
<strong>公众号：</strong> "shunwah星辰数智社"主理人。</p>
<p><strong>持有认证：</strong> OceanBase、MySQL、OpenGauss、崖山、金仓 KingBase、KaiwuDB、亚信 AntDBCA、翰高、GBase、Galaxybase、Neo4j、NebulaGraph、东方通 TongTech、TiDB 等多项权威认证。</p>
<p><strong>获奖经历：</strong> 崖山 YashanDB YVP、浪潮 KaiwuDB MVP、墨天轮 MVP、金仓社区 KVA、TiDB 社区 MVA、NebulaGraph 社区之星、IFClub 星珩联盟·智库星系技术专家、ITPUB 技术专家，社区版主及布道师。在 OceanBase&amp;墨天轮征文大赛、OpenGauss、TiDB、YashanDB、Kingbase、KWDB、Navicat 征文等赛事中多次斩获一、二、三等奖，原创技术文章常年被墨天轮、CSDN、ITPUB 等平台首页推荐。</p>
<p>📋 <strong>作者注：</strong></p>
<p>本文围绕 IvorySQL 5.3 容器化部署展开的安装过程、问题分析与排查方案，均基于 openEuler 服务器 + Docker 容器环境实测整理。文中涉及的 <code>docker run</code> 命令截断报错、<code>psql</code> 客户端缺失、数据库连接拒绝、磁盘空间不足导致的 PANIC 崩溃等问题，均为本次部署过程中真实踩坑记录。</p>
<p>数据库运行环境复杂多样，操作系统版本、Docker 版本、磁盘分区方案、网络环境都会影响最终表现。本文方案仅作技术参考，不代表唯一标准解法。文中使用的密码示例 <code>PKsjj@112!!</code> 和 <code>g4Bw#QPY4^t%87xTy</code> 均为演示用途，实际生产环境请使用强密码并妥善保管。</p>
<p>以上内容为个人经验总结，不构成线上生产环境落地建议。</p>
</blockquote>
<p>有问题欢迎在评论区留言，或者去 GitHub 上给 IvorySQL 社区提 issue。咱们下篇见。</p>]]></content:encoded>
            <category>IvorySQL</category>
            <category>Docker</category>
            <category>Mirror</category>
            <category>openEuler</category>
            <category>Troubleshooting</category>
            <category>PostgreSQL</category>
        </item>
        <item>
            <title><![CDATA[PostgreSQL/IvorySQL 私有云中实践]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/ivorysql-private-cloud</link>
            <guid>https://ivorySQL.org/zh-cn/blog/ivorysql-private-cloud</guid>
            <pubDate>Fri, 05 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[本文整理自 HOW 2026 中国数据库开源发展峰会暨 PostgreSQL 高峰论坛的演讲分享，演讲嘉宾：唐成，资深数据库架构师，中启乘数科技创始人及 CTO，《PostgreSQL修炼之道：从小工到专家》作者。]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>本文整理自 HOW 2026 中国数据库开源发展峰会暨 PostgreSQL 高峰论坛的演讲分享，演讲嘉宾：唐成，资深数据库架构师，中启乘数科技创始人及 CTO，《PostgreSQL修炼之道：从小工到专家》作者。</p>
</blockquote>
<p><a href="https://mp.weixin.qq.com/s/PVPEjr4lagmWleBnN34dpw" target="_blank" rel="noopener noreferrer" class="">HOW 2026 演讲 PPT 下载方式</a></p>
<p>该演讲录屏：<a href="https://www.bilibili.com/video/BV1jaLx63EZw/" target="_blank" rel="noopener noreferrer" class="">https://www.bilibili.com/video/BV1jaLx63EZw/</a></p>
<p>过去几年，"全面上云"一度成为数据库建设的主流方向。但随着核心业务规模不断扩大，越来越多企业开始重新关注私有云架构。尤其在金融、政企、运营商等场景下，数据库不仅需要云化能力，同时还要兼顾数据安全、资源可控、成本优化、国产化适配、高可用与稳定性。数据库私有云，也逐渐从"资源虚拟化平台"演变为完整的数据基础设施平台。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="为何选择-postgresql--ivorysql-构建私有云">为何选择 PostgreSQL / IvorySQL 构建私有云<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E4%B8%BA%E4%BD%95%E9%80%89%E6%8B%A9-postgresql--ivorysql-%E6%9E%84%E5%BB%BA%E7%A7%81%E6%9C%89%E4%BA%91" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="业务发展的挑战">业务发展的挑战<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E4%B8%9A%E5%8A%A1%E5%8F%91%E5%B1%95%E7%9A%84%E6%8C%91%E6%88%98" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>伴随业务场景复杂化、数据体量爆发式增长，传统数据库架构在业务快速增长过程中，逐渐暴露出多个问题：</p>
<ol>
<li class=""><strong>弹性能力不足</strong>：传统数据库架构扩容周期长，资源利用率低，难以适配业务高峰与低谷之间的动态变化。</li>
<li class=""><strong>运维复杂度持续提升</strong>：随着数据库实例规模扩大，人工部署、人工扩容、人工切换等传统运维方式已经难以支撑大规模数据库集群管理。</li>
<li class=""><strong>安全与合规压力增加</strong>：核心业务数据对数据主权、访问控制、审计监管提出更高要求，越来越多行业开始强调数据本地化与资源可控。</li>
<li class=""><strong>数据库成本持续增长</strong>：商业数据库授权费用、高端存储成本以及 DBA 运维成本不断增加，企业开始更加关注数据库总体拥有成本（TCO）。</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="私有云的优势与数据库选型考量">私有云的优势与数据库选型考量<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E7%A7%81%E6%9C%89%E4%BA%91%E7%9A%84%E4%BC%98%E5%8A%BF%E4%B8%8E%E6%95%B0%E6%8D%AE%E5%BA%93%E9%80%89%E5%9E%8B%E8%80%83%E9%87%8F" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>相比传统数据库部署模式，私有云更强调资源统一管理与平台化能力建设。在这一背景下，PostgreSQL 与 IvorySQL 成为了数据库私有云的重要选择。PostgreSQL 依靠成熟稳定的开源生态、丰富扩展能力以及企业级可靠性，已经成为当前最主流的开源数据库之一。而 IvorySQL 则在兼容 PostgreSQL 生态基础上，进一步增强 Oracle 兼容能力，更适合国产化替代与数据库迁移场景。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ivorysql-的优势">IvorySQL 的优势<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#ivorysql-%E7%9A%84%E4%BC%98%E5%8A%BF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>作为基于 PG 的开源数据库，100% 兼容 PG 并高度兼容 Oracle，为云原生而生，是企业构建私有云数据库的理想选择：</p>
<ol>
<li class=""><strong>100% 兼容 PostgreSQL</strong>：IvorySQL 基于最新的 PostgreSQL 版本进行研发，继承了 PG 的所有核心功能与优势。</li>
<li class=""><strong>高度兼容 Oracle</strong>：提供包括 PL/iSQL 语法、数据类型、函数及存储过程等的 Oracle 兼容性，大幅降低企业从 Oracle 迁移的成本与风险。</li>
<li class=""><strong>遵循 Apache 2.0 协议</strong>：采用宽松的开源协议，允许用户自由使用、修改和分发，提供了最大的技术自主权。</li>
<li class=""><strong>丰富的云原生生态</strong>：提供全套云原生工具链，支持在 K8s 等容器平台上快速部署、弹性伸缩和自动化运维。</li>
<li class=""><strong>全面的 Oracle 兼容性</strong>：新增对 ROWID、%ROWTYPE、%TYPE、NLS 参数、大小写敏感等 21 个 Oracle 关键特性的支持。</li>
<li class=""><strong>性能与稳定性提升</strong>：基于 PG 18.0 的异步 IO、跳跃扫描等新特性，提供卓越的性能和高并发处理能力。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="私有云架构设计">私有云架构设计<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E7%A7%81%E6%9C%89%E4%BA%91%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>数据库私有云的核心，本质上是基础设施能力的构建。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="基础设施规划裸金属服务器">基础设施规划——裸金属服务器<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E5%9F%BA%E7%A1%80%E8%AE%BE%E6%96%BD%E8%A7%84%E5%88%92%E8%A3%B8%E9%87%91%E5%B1%9E%E6%9C%8D%E5%8A%A1%E5%99%A8" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>对于高并发、高 IO 的核心业务，裸金属依然是数据库部署的重要方案。数据库直接运行在物理机上，可以绕过虚拟化层，获得更低的性能损耗。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/11-92599b46b5bc74acf9f6c633be8108bc.png" width="3799" height="976" class="img_ev3q"></p>
<p>在实际部署中，通常会重点关注 NVMe SSD、本地高性能存储、万兆 / 25G 网络、RDMA 网络。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/22-f621eeec208109ed923136c36507d73d.png" width="3919" height="1394" class="img_ev3q"></p>
<p>尤其是在 PostgreSQL 主备复制场景中，网络时延会直接影响同步效率。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="基础设施规划容器">基础设施规划——容器<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E5%9F%BA%E7%A1%80%E8%AE%BE%E6%96%BD%E8%A7%84%E5%88%92%E5%AE%B9%E5%99%A8" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>对于要求极致资源利用率的系统，采用 Kubernetes（K8S）作为容器编排平台，为 IvorySQL 实例提供一个弹性的运行基座。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/33-cd61557193a008ad1c5da4d90aa2e564.png" width="3925" height="1359" class="img_ev3q"></p>
<p>其中 <code>Operator</code> 是实现数据库高可用部署的核心组件。IvorySQL 适配自身数据库特性：</p>
<ul>
<li class="">Operator：<a href="https://github.com/IvorySQL/ivory-operator/" target="_blank" rel="noopener noreferrer" class="">https://github.com/IvorySQL/ivory-operator/</a></li>
<li class="">IvorySQL Cloud 前端：<a href="https://github.com/IvorySQL/ivory-cloud-web" target="_blank" rel="noopener noreferrer" class="">https://github.com/IvorySQL/ivory-cloud-web</a></li>
<li class="">IvorySQL Cloud 后端：<a href="https://github.com/IvorySQL/ivory-cloud" target="_blank" rel="noopener noreferrer" class="">https://github.com/IvorySQL/ivory-cloud</a></li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="高可用体系">高可用体系<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E9%AB%98%E5%8F%AF%E7%94%A8%E4%BD%93%E7%B3%BB" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>PostgreSQL 的高可用体系主要建立在流复制基础之上。在生产环境中，通常采用一主多备、同步复制、自动故障切换等方式保障数据库稳定运行。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/44-239eb50bd87b9ba574acbf9882a2239e.png" width="3989" height="1015" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="数据库性能优化">数据库性能优化<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E6%95%B0%E6%8D%AE%E5%BA%93%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>数据库性能优化并不是简单"调几个参数"。真正影响数据库性能的，通常包括操作系统、数据库参数、连接池管理、SQL 执行效率、存储 IO。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/55-77b47918894e03c8a2e6045248f5f37e.png" width="3924" height="1470" class="img_ev3q"></p>
<p>存储 IO 是数据库性能短板高发区，从 RAID 选型、文件系统、内核调度、磁盘分区四个维度落地优化：</p>
<ul>
<li class="">RAID 配置：机械盘配缓存 RAID 卡，优选 RAID10；NVMe 采用软 RAID</li>
<li class="">文件系统：生产环境统一使用 XFS，适配高并发与大文件 IO 场景</li>
<li class="">I/O 调度器：NVMe 配置 noop，普通磁盘选用 deadline，压低 IO 延迟</li>
<li class="">预读与对齐：分区对齐物理扇区，合理调控预读参数，削减冗余 IO 损耗</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="智能监控与运维">智能监控与运维<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E6%99%BA%E8%83%BD%E7%9B%91%E6%8E%A7%E4%B8%8E%E8%BF%90%E7%BB%B4" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="实时监控与告警">实时监控与告警<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E5%AE%9E%E6%97%B6%E7%9B%91%E6%8E%A7%E4%B8%8E%E5%91%8A%E8%AD%A6" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>数据库私有云环境中，监控体系的核心目标是快速发现问题、定位问题。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/66-f45e96e7746ac828262df64caf0095ec.png" width="3919" height="1291" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="构建智能监控">构建智能监控<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E6%9E%84%E5%BB%BA%E6%99%BA%E8%83%BD%E7%9B%91%E6%8E%A7" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>依托开源工具实现监控从被动告警到主动预判的升级：</p>
<ul>
<li class=""><strong>Metrics 采集与告警</strong>：使用 Prometheus Operator 部署 Prometheus 和 Alertmanager，通过 node_exporter 和 pg_exporter 采集指标</li>
<li class=""><strong>日志平台化管理</strong>：利用 EFK 或 Loki 等日志系统，集中收集和存储所有数据库实例的日志</li>
<li class=""><strong>链路追踪一体化</strong>：将数据库监控与 APM 系统打通，实现从应用端到数据库端的全链路追踪</li>
<li class=""><strong>异常检测与预测</strong>：利用机器学习算法对历史监控指标建模，实现基线预测和异常点检测</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="关键指标监控">关键指标监控<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E5%85%B3%E9%94%AE%E6%8C%87%E6%A0%87%E7%9B%91%E6%8E%A7" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>全面精准地采集数据库各项指标，重点关注：</p>
<ul>
<li class="">主机层指标：CPU、内存、磁盘 IO、网络吞吐量</li>
<li class="">实例层指标：活跃连接数、缓存命中率、TPS、QPS、锁等待数量</li>
<li class="">对象层指标：表的增删改查频次、索引使用率、表索引膨胀率、WAL 日志产生速度</li>
<li class="">会话与 SQL 指标：慢查询数量、长事务数量、空闲事务数量</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="获取数据库指标">获取数据库指标<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E8%8E%B7%E5%8F%96%E6%95%B0%E6%8D%AE%E5%BA%93%E6%8C%87%E6%A0%87" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>PostgreSQL 依靠内置系统视图采集细分性能指标：</p>
<ul>
<li class="">会话等待：<code>pg_wait_events</code>、<code>pg_stat_activity</code></li>
<li class="">WAL 运行：<code>pg_stat_wal</code></li>
<li class="">检查点：<code>pg_stat_checkpointer</code></li>
<li class="">异步 IO：<code>pg_aios</code></li>
<li class="">内存与 NUMA：<code>pg_stat_memory_usage</code>、<code>pg_shmem_allocations_numa</code></li>
<li class="">向量运算：<code>pg_stat_vector_ops</code></li>
<li class="">并行查询：<code>pg_stat_parallel_queries</code></li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="自动化运维实践">自动化运维实践<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E5%AE%9E%E8%B7%B5" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>随着数据库规模不断扩大，自动化运维已经成为数据库私有云的重要组成部分。目前常见实践包括基础设施即代码、配置管理自动化、CI/CD 流水线。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/77-5b70b77cbad25424749f804b1e4dd485.png" width="3493" height="1521" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="总结">总结<a href="https://ivorysql.org/zh-cn/blog/ivorysql-private-cloud#%E6%80%BB%E7%BB%93" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>随着企业核心业务持续向云化演进，数据库私有云建设已经不再只是简单的"数据库上云"。从基础设施规划到高可用体系建设，从性能优化到监控与自动化运维，数据库平台正在逐渐向云原生化、平台化、智能化方向发展。</p>
<p>PostgreSQL 凭借稳定性、扩展能力与成熟开源生态，已经成为数据库私有云的重要技术底座。而 IvorySQL 在兼容 PostgreSQL 生态的基础上，进一步增强 Oracle 兼容能力与云原生适配能力，也为企业数据库国产化与平滑迁移提供了更多可能。</p>
<p>从人工运维转向自动化运维，从被动告警转向智能预警，从单实例管理转向统一平台化管理，数据库运维体系正在发生深刻变化。</p>
<p>未来，随着 AI 运维、自助式数据库服务以及多模数据能力持续发展，数据库私有云也将逐渐演变为完整的数据基础设施平台。</p>]]></content:encoded>
            <category>IvorySQL</category>
            <category>PostgreSQL</category>
            <category>Private Cloud</category>
            <category>HOW2026</category>
            <category>Cloud Native</category>
        </item>
        <item>
            <title><![CDATA[HOW 2026 分论坛演讲：开源一套自用 IvorySQL 数据库 Skills]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/ivorysql-skills-how2026</link>
            <guid>https://ivorySQL.org/zh-cn/blog/ivorysql-skills-how2026</guid>
            <pubDate>Sat, 02 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[本文作者严少安，PostgreSQL ACE, IvorySQL 贡献者，IvorySQL 专家顾问委员。持有 PGCM, PGCE, PGCA, HGCP, HGCA 等认证。主笔公众号「少安事务所」，专注于数据 & AI 领域技术传播。本文为 HOW 2026 严少安老师的演讲内容，首发于公众号【少安事务所】。]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>本文作者严少安，PostgreSQL ACE, IvorySQL 贡献者，IvorySQL 专家顾问委员。持有 PGCM, PGCE, PGCA, HGCP, HGCA 等认证。主笔公众号「少安事务所」，专注于数据 &amp; AI 领域技术传播。本文为 HOW 2026 严少安老师的演讲内容，首发于公众号【少安事务所】。</p>
</blockquote>
<p>4 月 27 日，HOW 2026 大会在济南山东大厦召开。上午主论坛的内容回顾戳这里:</p>
<p><a href="https://mp.weixin.qq.com/s/HTs41c_nKX2I83c8LAZ2vw" target="_blank" rel="noopener noreferrer" class="">HOW2026 大会：PostgreSQL 与 AI 在泉城济南的硬核碰撞</a></p>
<p>笔者作为 IvorySQL 专家顾问委员，在当天下午分论坛进行了一次分享，期间首次介绍了 <strong>IvorySQL Skills</strong>。这是我把自己日常工作中沉淀的 IvorySQL 运维和开发技巧做了一次梳理，并开源出来分享给大家。下面来具体聊聊这套 IvorySQL-Skills 是什么，为什么需要它。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/img-1-f7d5b6f4aa6dfd24749443f818ed4480.png" width="1080" height="514" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="01-agentic-ai-时代告诉我怎么做比告诉我原理更值钱">01. Agentic AI 时代，"告诉我怎么做"比"告诉我原理"更值钱<a href="https://ivorysql.org/zh-cn/blog/ivorysql-skills-how2026#01-agentic-ai-%E6%97%B6%E4%BB%A3%E5%91%8A%E8%AF%89%E6%88%91%E6%80%8E%E4%B9%88%E5%81%9A%E6%AF%94%E5%91%8A%E8%AF%89%E6%88%91%E5%8E%9F%E7%90%86%E6%9B%B4%E5%80%BC%E9%92%B1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>我在 PPT 里放了三句话，这也是我做这个项目的初衷。</p>
<p><strong>第一，AI 适合做探索，不适合做决策。</strong> 让 AI 帮你写初稿、生成测试用例、解释执行计划都可以，但最终上生产的决策必须人把关。可问题是，如果你连"正确的做法"都不知道，你怎么判断 AI 给的建议好不好？</p>
<p><strong>第二，AI 是放大器，不是替身。</strong> 你本身能力越强，AI 放大效果越好。你对 SQL 优化理解不深，AI 的建议你也判断不了好坏。所以基础技能库必须先行。</p>
<p><strong>第三，选对工具比用多少重要。</strong> 不是所有 AI 工具都值得尝试，但一套经过生产环境验证的、即插即用的技术"菜谱"，绝对值得放在手边。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/img-2-1cc89ab4937223dc55a61bb317beba0f.png" width="1080" height="601" class="img_ev3q"></p>
<p>IvorySQL Skills 的定位就是这个"技术菜谱"。它不是官方文档的重复，而是我在过去两年从 Oracle 19c 迁移到 IvorySQL、在信创环境（龙芯 LoongArch + 麒麟 OS）做部署、给团队做内部培训过程中，反复打磨验证过的实操脚本和配置模板。</p>
<p><strong>项目地址</strong>：<a href="https://github.com/shawn0915/ivorysql-skills" target="_blank" rel="noopener noreferrer" class="">https://github.com/shawn0915/ivorysql-skills</a></p>
<p>欢迎大家点点 Star ⭐</p>
<p>如果访问速度慢，可以访问国内镜像：</p>
<p><a href="https://gitcode.com/mydb/ivorysql-skills" target="_blank" rel="noopener noreferrer" class="">https://gitcode.com/mydb/ivorysql-skills</a></p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/img-3-c99c079a89a3cba8ce1ea8b98c3b38a8.png" width="1080" height="598" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="02-ivorysql-skills-里都有什么">02. IvorySQL-Skills 里都有什么<a href="https://ivorysql.org/zh-cn/blog/ivorysql-skills-how2026#02-ivorysql-skills-%E9%87%8C%E9%83%BD%E6%9C%89%E4%BB%80%E4%B9%88" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>目前整个套件按照使用场景拆成了四大模块，共 <strong>22 个技能</strong>。每个 skill 都是一个独立文件，包含场景描述、适用版本、操作步骤和验证命令。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/img-4-acab12b7596b8db95029099e5c463d7e.png" width="1080" height="508" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="核心技能">核心技能<a href="https://ivorysql.org/zh-cn/blog/ivorysql-skills-how2026#%E6%A0%B8%E5%BF%83%E6%8A%80%E8%83%BD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<table><thead><tr><th>技能</th><th>说明</th></tr></thead><tbody><tr><td>ivorysql</td><td>主技能入口，产品概览</td></tr><tr><td>ivorysql-deployment</td><td>安装部署（Docker/K8s/源码）</td></tr><tr><td>ivorysql-architecture</td><td>架构设计（双 Parser/双端口）</td></tr><tr><td>ivorysql-config</td><td>系统配置（GUC/参数）</td></tr><tr><td>ivorysql-security</td><td>安全配置（权限/审计/TDE）</td></tr><tr><td>ivorysql-ha</td><td>高可用（备份/复制/监控）</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="开发技能">开发技能<a href="https://ivorysql.org/zh-cn/blog/ivorysql-skills-how2026#%E5%BC%80%E5%8F%91%E6%8A%80%E8%83%BD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<table><thead><tr><th>技能</th><th>说明</th></tr></thead><tbody><tr><td>ivorysql-app-builder</td><td>端到端应用开发</td></tr><tr><td>ivorysql-jdbc</td><td>Java JDBC 开发</td></tr><tr><td>ivorysql-python</td><td>Python 开发（psycopg/SQLAlchemy）</td></tr><tr><td>ivorysql-go</td><td>Go 开发（pgx/GORM）</td></tr><tr><td>ivorysql-csharp</td><td>C#/.NET 开发（Dapper/EF Core）</td></tr><tr><td>ivorysql-nodejs</td><td>Node.js 开发（pg/Sequelize/Prisma）</td></tr><tr><td>ivorysql-c</td><td>C 语言开发（libpq/ECPG）</td></tr><tr><td>ivorysql-odbc</td><td>ODBC 跨平台开发</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="功能技能">功能技能<a href="https://ivorysql.org/zh-cn/blog/ivorysql-skills-how2026#%E5%8A%9F%E8%83%BD%E6%8A%80%E8%83%BD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<table><thead><tr><th>技能</th><th>说明</th></tr></thead><tbody><tr><td>ivorysql-sql</td><td>SQL 语法（DDL/DML/查询）</td></tr><tr><td>ivorysql-functions</td><td>内置函数（字符串/日期/聚合）</td></tr><tr><td>ivorysql-plisql</td><td>PL/iSQL 存储过程</td></tr><tr><td>ivorysql-oracle-compat</td><td>Oracle 兼容特性（21 项）</td></tr><tr><td>ivorysql-migration</td><td>数据迁移（Oracle/MySQL → IvorySQL）</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="辅助技能">辅助技能<a href="https://ivorysql.org/zh-cn/blog/ivorysql-skills-how2026#%E8%BE%85%E5%8A%A9%E6%8A%80%E8%83%BD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<table><thead><tr><th>技能</th><th>说明</th></tr></thead><tbody><tr><td>ivorysql-faq</td><td>常见问题</td></tr><tr><td>ivorysql-ecosystem</td><td>生态集成</td></tr><tr><td>ivorysql-tools</td><td>客户端工具</td></tr></tbody></table>
<p><strong>示例：Oracle 兼容函数速查（迁移适配类）</strong></p>
<p>IvorySQL 5.0 基于 PostgreSQL 18.0 内核，新增了 21 个 Oracle 兼容特性。但开发者在实际迁移时，经常卡在一些具体函数上。这个 skill 不是罗列所有兼容函数，而是只放"最容易踩坑"的 Top 20 对照表。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">-- 场景：从Oracle 19c迁移到IvorySQL 5.3，快速验证常用函数兼容性</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">-- 适用版本：IvorySQL 5.0+ / PG 18.x</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">-- 1. 日期处理：Oracle的ADD_MONTHS</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SELECT ADD_MONTHS('2026-04-27'::timestamp, 3);  -- IvorySQL直接支持</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">-- 2. 字符串拼接：Oracle的||</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SELECT 'HOW' || '2026' || 'Jinan';  -- 兼容，无需改写</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">-- 3. 空值处理：Oracle的NVL</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SELECT NVL(NULL, 'default_value');  -- 兼容</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">-- 4. 行号：Oracle的ROWNUM</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SELECT * FROM (SELECT *, ROWNUM() OVER() AS rn FROM pg_tables) t WHERE rn &lt;= 5;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">-- 验证命令：查看当前IvorySQL兼容级别</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SHOW ivorysql.compatible_mode;  -- 返回 oracle 或 pg</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="03-一个好的-skill-应该是什么样子">03. 一个好的 Skill 应该是什么样子<a href="https://ivorysql.org/zh-cn/blog/ivorysql-skills-how2026#03-%E4%B8%80%E4%B8%AA%E5%A5%BD%E7%9A%84-skill-%E5%BA%94%E8%AF%A5%E6%98%AF%E4%BB%80%E4%B9%88%E6%A0%B7%E5%AD%90" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>在 HOW 2026 现场，我也分享了 IvorySQL Skills 的设计规范。这不是什么高大上的方法论，就是四个硬性要求，确保每个 skill 都是"拿到就能用"的水平。</p>
<table><thead><tr><th>设计原则</th><th>具体要求</th></tr></thead><tbody><tr><td>场景驱动</td><td>标题必须包含"场景"和"适用版本"，如"如何编译安装 IvorySQL 5.3"</td></tr><tr><td>即插即用</td><td>代码块必须能直接复制运行，变量用 <code>&lt;&gt;</code> 标注，注释说明替换内容</td></tr><tr><td>信创适配</td><td>凡是涉及安装部署的 skill，必须标注支持的国产 CPU 和 OS</td></tr><tr><td>可验证</td><td>每个 skill 最后必须跟一个验证命令或检查清单，确保操作成功</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="04-怎么安装使用三种姿势30-秒上手">04. 怎么安装使用：三种姿势，30 秒上手<a href="https://ivorysql.org/zh-cn/blog/ivorysql-skills-how2026#04-%E6%80%8E%E4%B9%88%E5%AE%89%E8%A3%85%E4%BD%BF%E7%94%A8%E4%B8%89%E7%A7%8D%E5%A7%BF%E5%8A%BF30-%E7%A7%92%E4%B8%8A%E6%89%8B" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>除了下面介绍的三种常规方法外，还有更简单的方式。直接告诉你的 AI 编程助手：</p>
<blockquote>
<p>帮我安装这个 skills：shawn0915/ivorysql-skills</p>
</blockquote>
<p><strong>方式一：npx 一键安装（推荐，适合快速体验）</strong></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># 全局安装skills管理器后，一键拉取IvorySQL技能包</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">npx skills add shawn0915/ivorysql-skills</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 安装完成后，查看可用skill列表</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">skills list | grep ivorysql</span><br></span></code></pre></div></div>
<p><strong>方式二：npm 一键安装</strong></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">npm i ivorysql-skills</span><br></span></code></pre></div></div>
<p><strong>方式三：Git 克隆（适合二次开发和团队内部定制）</strong></p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">git clone https://github.com/shawn0915/ivorysql-skills.git</span><br></span></code></pre></div></div>
<p>目录结构：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ivorysql-skills/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── SKILL.md                      # 包级入口（路由中心）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── README.md                     # 说明文档</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── ivorysql/                    # 主技能（产品概览）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── references/              # 参考文档</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── ivorysql-app-builder/        # 端到端应用开发</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── references/              # 参考文档</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── ivorysql-architecture/        # 架构设计</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── references/              # 参考文档</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...</span><br></span></code></pre></div></div>
<p>在内部环境可以这样用：把 Git 仓库 fork 到公司内网 GitLab，DBA 和开发各自贡献自己验证过的 skill，形成团队级的"私有技能库"。新人入职第一天，或者当一个新的 Agent 上线，先装这个技能包，基本能覆盖大部分日常操作。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="05-结语">05. 结语<a href="https://ivorysql.org/zh-cn/blog/ivorysql-skills-how2026#05-%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>在分享的最后，我聊了一个观点：<strong>Agentic AI 时代，Skills 不是被 AI 替代的东西，而是给 AI 提供"事实基准"的锚点。</strong></p>
<p>什么意思呢？你让 AI 直接生成一条 IvorySQL 的备份脚本，它可能给你一条 pg_basebackup 的命令，参数可能适用于 PG 15 但不适用于 PG 18，或者没考虑国密 SSL 环境。但如果你已经让 Agent 安装了 IvorySQL Skills，那么大模型生成的内容就有了边界。</p>
<p>一上午听完 HOW 2026 主论坛，下午又和各位同行交流，最大的感受是：<strong>中国 PG 生态正在从"追逐开源"走向"贡献开源"。</strong> IvorySQL Skills 这个项目很小，没有复杂代码，就是一堆 Markdown 文档，但它本质上是为了让大家在使用 AI 编程助手时，更加容易上手，让 Agent 更懂你，让 Agent 更懂 IvorySQL。</p>
<p><strong>Skills 是经验的"蒸馏"品，是 Agent 的行动指南，也是你提升工作效率的"硬菜"。</strong></p>
<blockquote>
<p><strong>2026-05-02 批注</strong></p>
</blockquote>
<blockquote>
<p>本文是在 HOW 2026 第二天写的，也就是 4 月 28 日，但是我在 4 月 30 日看到了 <a href="https://mp.weixin.qq.com/s/GMqlICHS2dlmPQxPAx7cow" target="_blank" rel="noopener noreferrer" class="">Oracle Skills 开源了</a>，然后意识到 IvorySQL-Skills 还有很多需要改进的地方。比如，IvorySQL 和 PostgreSQL 的差异点，是否需要再梳理一下。再如，IvorySQL 在 Oracle 兼容性上，以及从 Oracle 迁移到 IvorySQL 还有哪些案例、脚本、流程、工具等都需要补充优化。</p>
</blockquote>
<blockquote>
<p>未完待续。</p>
</blockquote>]]></content:encoded>
            <category>IvorySQL</category>
            <category>Skills</category>
            <category>AI Agent</category>
            <category>PostgreSQL</category>
            <category>HOW2026</category>
        </item>
        <item>
            <title><![CDATA[IvorySQL 5.0+：助力 Oracle 平滑过渡至 PostgreSQL 的里程碑式产品]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/ivorysql-5.0-oracle-to-postgresql-migration</link>
            <guid>https://ivorySQL.org/zh-cn/blog/ivorysql-5.0-oracle-to-postgresql-migration</guid>
            <pubDate>Tue, 10 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[IvorySQL：一种“先进、功能完备、开源且兼容 Oracle 的 PostgreSQL 数据库，始终致力于保持 100% 兼容性，并可作为最新 PostgreSQL 的无缝替代”。]]></description>
            <content:encoded><![CDATA[<p><a href="https://github.com/IvorySQL/IvorySQL" target="_blank" rel="noopener noreferrer" class="">IvorySQL</a>：一种“先进、功能完备、开源且兼容 Oracle 的 PostgreSQL 数据库，始终致力于保持 100% 兼容性，并可作为最新 PostgreSQL 的无缝替代”。</p>
<p>同时，这款引擎也是我们团队将各类外部 SQL 语句及应用适配至 PostgreSQL 时的首选工具！我们秉持平滑过渡、而非整体迁移的理念 —— 让旧数据库引擎向新引擎逐步切换，核心为降低迁移风险、优化实施效果。借助这类方案，能为实现这两大目标带来关键助力。</p>
<p>尽管 IvorySQL 最新主版本发布已有一段时间，但其中的全新特性依旧让我们倍感振奋，特此为大家分享。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="新特性概览">新特性概览<a href="https://ivorysql.org/zh-cn/blog/ivorysql-5.0-oracle-to-postgresql-migration#%E6%96%B0%E7%89%B9%E6%80%A7%E6%A6%82%E8%A7%88" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL 5.0 版本（<a href="https://github.com/IvorySQL/IvorySQL/releases/tag/IvorySQL_5.0" target="_blank" rel="noopener noreferrer" class="">5.0 发布说明</a>）于 2025 年 11 月 25 日发布，随后在 2025 年 12 月 18 日推出 5.1 版本（<a href="https://github.com/IvorySQL/IvorySQL/releases/tag/IvorySQL_5.1" target="_blank" rel="noopener noreferrer" class="">5.1 发布说明</a>）。这两个版本凝聚了 IvorySQL 团队的大量投入，不仅带来了多项高品质的功能优化，还完成了对 PostgreSQL 18 的兼容性适配。</p>
<p>IvorySQL v5 带来了一系列关键能力，包括：</p>
<ul>
<li class=""><strong>PLiSQL</strong> —— 兼容 Oracle PL/SQL 的子集</li>
<li class="">Oracle 兼容包支持</li>
<li class="">Oracle 风格序列支持</li>
<li class=""><strong>v5.0</strong> 增强能力包括：<code>ROWID</code>、<code>%TYPE</code>、<code>%ROWTYPE</code> 以及嵌套子函数</li>
</ul>
<p>我们尤其喜欢很多功能升级。</p>
<p>例如，IvorySQL 现在改进了 <code>NULL</code> 值处理逻辑，在兼容模式下（与 Oracle 的行为一致），<code>NULL</code> 值现在被视为空字符串，以避免迁移过程中出现错误。以 <code>SELECT CONCAT ('a' || NULL)</code> 语句为例，IvorySQL 将返回结果 <code>'a'</code>，而非沿用 PostgreSQL 默认的 <code>NULL</code> 值返回逻辑。</p>
<p>我们非常喜欢的一项增强功能是：你现在可以嵌套函数和存储过程。函数能够嵌入到其他函数内部（类似于 Oracle 的包，但更简单，仅使用私有方法）。这让你能够在一个地方集中组织复杂的逻辑。</p>
<p>与之类似，系统还增加了对 <code>DO [ LANGUAGE lang_name ] 代码块 [USING IN | OUT | IN OUT, ...]</code> 语法的支持。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="实测验证">实测验证<a href="https://ivorysql.org/zh-cn/blog/ivorysql-5.0-oracle-to-postgresql-migration#%E5%AE%9E%E6%B5%8B%E9%AA%8C%E8%AF%81" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>目前，我们已基于该版本完成多类应用的迁移适配测试，适配对象规模跨度极大：既有仅包含少量包、存储过程及函数的轻量应用（约 10-50 个数据库对象），也有包含海量数据库对象的大型业务系统（约 5 万个对象，其中存储过程超 1 万个、数据库包达数百个）。</p>
<p>尽管后续仍需持续迭代、补充更多功能特性，但当前版本已能大幅降低从 Oracle 向 PostgreSQL 平滑过渡的实施成本，助力迁移工作高效落地。</p>
<p>若你计划亲自体验测试，IvorySQL 提供了丰富的快速上手方式，包括源码编译、容器部署等，甚至还支持 WASM 构建！WASM 的独特优势在于，无需在本地完成完整安装，即可在浏览器中直接运行 IvorySQL，便于快速验证语法兼容性并开展 PostgreSQL 体系的初步探索。</p>
<p>你可参照 IvorySQL 官方发布的<a href="https://www.ivorysql.org/blog/ivorysql-wasm/" target="_blank" rel="noopener noreferrer" class="">相关文章</a>，通过简单几步完成 IvorySQL-WASM 项目的本地部署。也可直接访问 IvorySQL 官网，体验<a href="https://trial.ivorysql.org/" target="_blank" rel="noopener noreferrer" class="">在线托管的 WASM 版本</a>。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="后续规划">后续规划<a href="https://ivorysql.org/zh-cn/blog/ivorysql-5.0-oracle-to-postgresql-migration#%E5%90%8E%E7%BB%AD%E8%A7%84%E5%88%92" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>我们团队很高兴聘用 IvorySQL 项目的贡献者，目前我们团队已有多位贡献者加入 IvorySQL 的核心研发工作。其中 Cédric Villemain、Yasir Hussain Shah 等成员的贡献，已在 5.0 和 5.1 版本的发布说明中专门致谢 —— 未来我们还将吸纳更多优秀开发者参与项目建设。</p>
<p>针对下一个版本，我们已规划了多项重磅新特性开发工作，具体包括：</p>
<ul>
<li class=""><code>ENABLE / DISABLE</code> 约束语法支持</li>
<li class=""><code>UTL_FILE</code> 包新增适配</li>
<li class="">Oracle 风格的 <code>CREATE TRIGGER</code> 触发器体（无需预先创建函数）</li>
<li class="">支持 Oracle 旧式连接运算符 <code>(+)</code></li>
</ul>
<p>我们同样期待社区为下一个版本持续贡献的研发成果落地。目前已有多项优质功能正处于积极开发与合入阶段，例如项目新晋贡献者 Rophy Tsai 近期新增了 <code>DBMS_OUTPUT</code> 和 <code>DBMS_UTILITY</code> 包的适配支持 —— 这两个包是 Oracle 生态 SQL 代码库中应用极为广泛的工具包。该特性现已合入 IvorySQL 主分支，预计将随下一个小版本或主版本正式发布。</p>
<p>对于希望在无需对现有数据库逻辑和应用程序进行大量改造的前提下，完成向 PostgreSQL 迁移或探索 PostgreSQL 生态的开发者而言，IvorySQL（尤其是 5.x 系列版本）提供了一套切实可行的解决方案。直接迁移至 PostgreSQL 在部分场景下，可能需要对数据库对象和应用代码做出大量调整，而 IvorySQL 能够有效填补这一适配鸿沟，凭借更高的兼容性，助力开发者更平滑地完成 PostgreSQL 生态的落地与适配。</p>
<p>您还可以通过以下渠道深入了解 IvorySQL：</p>
<ul>
<li class="">官网 blog：<a href="https://www.ivorysql.org/zh-CN/blog" target="_blank" rel="noopener noreferrer" class="">https://www.ivorysql.org/zh-CN/blog</a></li>
<li class="">社区活动：<a href="https://www.ivorysql.org/zh-cn/events" target="_blank" rel="noopener noreferrer" class="">https://www.ivorysql.org/zh-cn/events</a></li>
<li class="">HOW 2025 相关会议录像：<a href="https://www.youtube.com/@ivorysql" target="_blank" rel="noopener noreferrer" class="">https://www.youtube.com/@ivorysql</a></li>
</ul>
<p>值得一提的是，2026 年的 HOW 大会已确定于 4 月 26 日至 28 日举行，<a href="https://jsj.top/f/uebqBc" target="_blank" rel="noopener noreferrer" class="">议题征集</a>截止日期为 2026 年 2 月 27 日。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="更多参考">更多参考<a href="https://ivorysql.org/zh-cn/blog/ivorysql-5.0-oracle-to-postgresql-migration#%E6%9B%B4%E5%A4%9A%E5%8F%82%E8%80%83" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://www.postgresql.org/about/news/ivorysql-50-released-major-oracle-compatibility-expansion-on-postgresql-180-foundation-3180/" target="_blank" rel="noopener noreferrer" class="">PostgreSQL.org 发布的 IvorySQL 5.0 公告</a></li>
<li class=""><a href="https://github.com/orgs/IvorySQL/projects/19" target="_blank" rel="noopener noreferrer" class="">GitHub 上的 IvorySQL 5.0 路线图</a></li>
</ul>]]></content:encoded>
            <category>IvorySQL</category>
            <category>Database</category>
            <category>Oracle Compatible</category>
            <category>PostgreSQL</category>
            <category>migration</category>
        </item>
        <item>
            <title><![CDATA[聚焦六大功能：PostgreSQL 18 新特性深度解析]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/analysis-of-pg-18-key-new-features</link>
            <guid>https://ivorySQL.org/zh-cn/blog/analysis-of-pg-18-key-new-features</guid>
            <pubDate>Fri, 26 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[PostgreSQL 全球开发组于 2025 年 5 月 8 日发布了 PostgreSQL 18 的首个 Beta 版本，正式版也已于 9 月 25 日正式上线。本文 IvorySQL 社区将为大家拆解 PostgreSQL 18 的六大亮点特性。]]></description>
            <content:encoded><![CDATA[<p>PostgreSQL 全球开发组于 2025 年 5 月 8 日发布了 PostgreSQL 18 的首个 Beta 版本，正式版也已于 9 月 25 日正式上线。本文 IvorySQL 社区将为大家拆解 PostgreSQL 18 的六大亮点特性。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="一pg-异步-ioaio框架迈出打破同步阻塞瓶颈的第一步">一、PG 异步 I/O（AIO）框架：迈出打破同步阻塞瓶颈的第一步<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E4%B8%80pg-%E5%BC%82%E6%AD%A5-ioaio%E6%A1%86%E6%9E%B6%E8%BF%88%E5%87%BA%E6%89%93%E7%A0%B4%E5%90%8C%E6%AD%A5%E9%98%BB%E5%A1%9E%E7%93%B6%E9%A2%88%E7%9A%84%E7%AC%AC%E4%B8%80%E6%AD%A5" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>PostgreSQL 18 全新引入异步 I/O 子系统。新机制允许<strong>特定场景下</strong>并行执行多个异步预读操作，CPU 无需等待数据返回即可继续推进查询，一定程度上降低了等待损耗。此框架为 PG 未来更深入更彻底的异步 I/O 性能优化奠定基础，迈出了第一步。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="核心提升场景">核心提升场景<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E6%A0%B8%E5%BF%83%E6%8F%90%E5%8D%87%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p><strong>【当前仅实现异步读，没有实现异步写】</strong> 所有 Seq Scan 场景下通过适配过异步 I/O 的 ReadStream 设施可实现并行化顺序预读，提升 Seq Scan 的性能，效果好于原有 <code>posix_fadvice</code> 的建议性预读。尤其在云存储场景下单次阻塞读 I/O 相比本地 I/O 所需时间更长，异步 I/O 加持下的并行化预读的优势更加明显。目前异步 I/O 已支持顺序扫描、位图堆扫描和 VACUUM 操作的异步读取，早期测试显示，读取密集型查询性能可提升 2-3 倍。</p>
<p>如图所示：</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250926-aio-drawio-99a351b1a3a0b55a3452defb40d3eff1.png" width="2283" height="1503" class="img_ev3q"></p>
<p>使用了异步 I/O 的 ReadStream 机制可以在收到读请求后异步地预读后续可能使用的 buffer。而在使用同步 I/O 方式在每次请求读取 buffer 时，都要等待 I/O 操作完成，这样降低了系统吞吐量。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="使用方法">使用方法<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># - I/O -</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#backend_flush_after = 0		# measured in pages, 0 disables</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">effective_io_concurrency = 300		# 1-1000; 0 disables issuing multiple simultaneous IO requests</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">maintenance_io_concurrency = 300	# 1-1000; same as effective_io_concurrency</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">io_max_combine_limit = 256kB		# usually 1-128 blocks (depends on OS)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    # (change requires restart)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">io_combine_limit = 256kB		# usually 1-128 blocks (depends on OS)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">io_method = io_uring # worker, io_uring, sync</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    # (change requires restart)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">io_max_concurrency = 128		# Max number of IOs that one process</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    # can execute simultaneously</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    # -1 sets based on shared_buffers</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    # (change requires restart)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#io_workers = 3				# 1-32;</span><br></span></code></pre></div></div>
<p>用户可选择三种不同的 <code>io_method</code> 启用异步 I/O，分别是：</p>
<ul>
<li class=""><code>worker</code> 若干后台 I/O workers 接收处理后端进程的 I/O 请求。</li>
<li class=""><code>io_uring</code> Linux 系统中 io_uring 子系统通过操作系统内核线程处理 PG 的 I/O 请求。</li>
<li class=""><code>sync</code> 满足异步 I/O 框架接口要求的同步 I/O。</li>
</ul>
<p>要启用异步 I/O，用户需要根据自身情况设定上述 GUC 参数。其中每个进程能够拥有的最大异步 I/O 句柄为 <code>io_max_concurrency</code>，用户可以将其置 <code>-1</code>，使数据库自行选择合适的值。若自行选择的值太大，则可能因为异步 I/O 所占空间太大而无法启动数据库；若自行选择的值太小则无法完全发挥出异步 I/O 性能。</p>
<p>启动数据库后，用户可通过 <code>pg_aios</code> 视图实时地获取当前系统异步 I/O 执行状况:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">postgres=# select * from pg_aios;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">-[ RECORD 1 ]---+-------------------------------------------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pid             | 85834</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">io_id           | 14208</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">io_generation   | 204</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">state           | SUBMITTED</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">operation       | readv</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">off             | 116252672</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">length          | 8192</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">target          | smgr</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">handle_data_len | 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">raw_result      |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">result          | UNKNOWN</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">target_desc     | block 14191 in file "base/5/16427"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">f_sync          | f</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">f_localmem      | f</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">f_buffered      | t</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...</span><br></span></code></pre></div></div>
<p>各列含义详见<a href="https://www.postgresql.org/docs/18/view-pg-aios.html" target="_blank" rel="noopener noreferrer" class="">官方文档</a>。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="框架设计">框架设计<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E6%A1%86%E6%9E%B6%E8%AE%BE%E8%AE%A1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>PG 18 引入异步 I/O 框架，支持通过 GUC 参数灵活配置异步 I/O：包括实现方式（<code>io_method</code>，可选 worker、io_uring 或 sync）、并发规模（如 <code>*_io_concurrency</code>、<code>io_max_concurrency</code>）及实现相关参数（如 <code>io_workers</code>）。</p>
<p>该框架对 I/O 目标（当前支持 smgr，未来计划支持 WAL）和不同阶段、不同数据源(shared buffer/local buffer)的行为进行了抽象（通过 <code>PgAioHandleCallbacks</code> 结构），以支持后续扩展。相关内存于启动时在共享内存中分配，后续不再缩放。进程按编号访问所属异步 I/O 资源，句柄通过 generation 号标记复用。</p>
<p>目前该版本异步 I/O 主要提供对 smgr 的异步读支持，尚不支持 WAL 异步读写，smgr 的异步写入功能仍在开发中。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="对原有设施的修改">对原有设施的修改<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%AF%B9%E5%8E%9F%E6%9C%89%E8%AE%BE%E6%96%BD%E7%9A%84%E4%BF%AE%E6%94%B9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ol>
<li class=""><strong>扩展 smgr 接口</strong>：新增 <code>smgr_startreadv</code> 方法以支持异步读取。</li>
<li class=""><strong>实现回调结构</strong>：smgr 需实现 <code>PgAioTargetInfo</code> 和 <code>PgAioHandleCallBacks</code> 回调结构。</li>
<li class=""><strong>适配现有模块</strong>：smgr 和 buffer manager 等模块需填充异步 I/O 抽象结构以兼容框架。</li>
<li class=""><strong>PG 临界区处理</strong>：同步 I/O 可在 PG 临界区内发起；异步 I/O 因分段执行且带回调，需移除回调中可能失败的操作（临界区内一切操作不能失败，如用<code>RelPathStr</code>替代 palloc 的<code>char*</code>字符串）以确保安全。</li>
<li class=""><strong>优化上层接口</strong>：利用异步 I/O 改造 ReadStream 等接口，实现真预读，大幅提升顺序扫描、pg_prewarm 及 ANALYZE 等操作的 I/O 性能，效果优于原有 posix_fadvise 方案。</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="使用注意及未来展望">使用注意及未来展望<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E5%8F%8A%E6%9C%AA%E6%9D%A5%E5%B1%95%E6%9C%9B" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ol>
<li class="">**io_uring 需要较新内核：**旧版 Linux Kernel 不支持 io_uring。某些早期版本内核虽然支持 io_uring，但功能和性能表现与新内核有一定差距。</li>
<li class=""><strong>架构限制并发粒度</strong>：受多进程架构所限，PG 异步 I/O 期间可并行运行的计算任务较少，难以实现更细粒度的任务级异步。当前主要性能收益集中于<strong>ReadStream 顺序预读</strong>及等并行 I/O 操作。</li>
<li class=""><strong>未来可能的性能提升</strong>：Linux io_uring 支持直接 I/O（DIO）特性，为在 PG 中启用 DIO 奠定基础。未来启用直接 I/O 可免除双重 buffer(OS 层面对 I/O 数据进行 buffer，PG 层面对 I/O 进行 buffer)以减少不必要的数据复制。在高速 NVMe 上还配合 DIO 启用<strong>IORING_SETUP_IOPOLL</strong>选项使用轮训方式检查 I/O 完成情况，还可以进一步提升性能。</li>
<li class=""><strong>未来更多的异步 I/O 后端</strong>：除了<code>sync</code>模式，正式版的 PostgreSQL 18 仅支持 <code>worker</code> 和 <code>io_uring</code> 两种异步 I/O 后端。目前异步 I/O 框架设计已基本完备，未来版本有望支持 Windows IORing，IOCP，以及 Posix 异步 I/O 等 I/O 后端，为用户提供更多选择。</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="小结">小结<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%B0%8F%E7%BB%93" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>PostgreSQL 18 异步 I/O 框架提升数据库系统 I/O 能力，同时也增强了 PostgreSQL 架构的可扩展性，用户只需要根据自身情况修改 GUC 参数即可获取到异步 I/O 带来的好处。目前异步 I/O 框架设计基本完备，后期支持其他异步 I/O 后端也将非常方便。任意平台 Postgres 用户可以尝试 <code>io_method = worker</code>或者<code>sync</code> 若要在非官方适配 io_uring 的旧 Linux 内核发行版上使用 io_uring 后端，需要进行充分测试后再使用。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="二-跳跃式扫描让-b-树索引-提速换挡">二、 跳跃式扫描：让 B 树索引 “提速换挡”<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E4%BA%8C-%E8%B7%B3%E8%B7%83%E5%BC%8F%E6%89%AB%E6%8F%8F%E8%AE%A9-b-%E6%A0%91%E7%B4%A2%E5%BC%95-%E6%8F%90%E9%80%9F%E6%8D%A2%E6%8C%A1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>在 PostgreSQL 18 之前的版本中，多列 B 树索引可用于包含该索引中任意子集列的查询条件，在对起始（最左侧）列施加约束时最为高效。对前导列的等式约束，加上对第一个不带有等式约束的列的任何不等式约束，用于限制要扫描的索引部分。</p>
<p>例如，给定一个基于（a，b，c）非空字段的升序索引和查询条件 WHERE a = 5 AND b &gt;= 42 AND c &lt; 77，该索引将从具有 a = 5 和 b = 42 的第一个条目开始扫描，一直扫描到最后具有 a = 5 的条目。 具有 c &gt;= 77 的索引条目将被跳过，但它们仍需扫描。</p>
<p>原则上，这种索引可以用于对 b 和/或 c 有约束条件而对 a 没有约束条件的查询——但必须扫描整个索引，所以在大多数情况下优化器更倾向于对表进行顺序扫描表，而非利用索引扫描。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="核心提升场景-1">核心提升场景<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E6%A0%B8%E5%BF%83%E6%8F%90%E5%8D%87%E5%9C%BA%E6%99%AF-1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>从 PostgreSQL 18 开始：</p>
<p>如果 B 树索引扫描能够应用跳跃式扫描（SKIP SCAN），在遍历索引时应用每个列的约束，可以减少索引的读取。跳跃式扫描的工作原理是内部生成一个动态等式约束，该约束与索引列中的每个可能值相匹配。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="效果测试">效果测试<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E6%95%88%E6%9E%9C%E6%B5%8B%E8%AF%95" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>对比版本：</p>
<p><strong>PostgreSQL 17 vs PostgreSQL 18</strong></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="表结构与索引">表结构与索引<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E8%A1%A8%E7%BB%93%E6%9E%84%E4%B8%8E%E7%B4%A2%E5%BC%95" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> t1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">c1 </span><span class="token keyword" style="color:#00009f">int</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">c2 </span><span class="token keyword" style="color:#00009f">int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">c3 </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">WITH</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">fillfactor</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">80</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INDEX</span><span class="token plain"> idx_t1_c1c2 </span><span class="token keyword" style="color:#00009f">ON</span><span class="token plain"> t1</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> c2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="数据生成">数据生成<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E6%95%B0%E6%8D%AE%E7%94%9F%E6%88%90" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> t1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">*</span><span class="token number" style="color:#36acaa">1000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">::</span><span class="token keyword" style="color:#00009f">int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">*</span><span class="token number" style="color:#36acaa">10000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">::</span><span class="token keyword" style="color:#00009f">int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> random</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> generate_series</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token number" style="color:#36acaa">1000000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> g</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="通过-copy-导入数据">通过 COPY 导入数据<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E9%80%9A%E8%BF%87-copy-%E5%AF%BC%E5%85%A5%E6%95%B0%E6%8D%AE" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">COPY t1 </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/.../t1.csv'</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">WITH</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">FORMAT csv</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="查询语句-使用复合索引的第二个列">查询语句 使用复合索引的第二个列<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E6%9F%A5%E8%AF%A2%E8%AF%AD%E5%8F%A5-%E4%BD%BF%E7%94%A8%E5%A4%8D%E5%90%88%E7%B4%A2%E5%BC%95%E7%9A%84%E7%AC%AC%E4%BA%8C%E4%B8%AA%E5%88%97" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">EXPLAIN</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">ANALYZE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> t1 </span><span class="token keyword" style="color:#00009f">WHERE</span><span class="token plain"> c2</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="postgresql-17-执行计划-选择使用并行顺序扫描">PostgreSQL 17 执行计划 选择使用并行顺序扫描<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#postgresql-17-%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92-%E9%80%89%E6%8B%A9%E4%BD%BF%E7%94%A8%E5%B9%B6%E8%A1%8C%E9%A1%BA%E5%BA%8F%E6%89%AB%E6%8F%8F" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">                                                    QUERY PLAN</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">-------------------------------------------------------------------------------------------------------------------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Gather  (cost=1000.00..12986.33 rows=100 width=16) (actual time=1.125..76.076 rows=90 loops=1)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Workers Planned: 2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Workers Launched: 2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   -&gt;  Parallel Seq Scan on t1  (cost=0.00..11976.33 rows=42 width=16) (actual time=1.414..68.624 rows=30 loops=3)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         Filter: (c2 = 100)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         Rows Removed by Filter: 333303</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Planning Time: 0.792 ms</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Execution Time: 76.165 ms</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">(8 rows)</span><br></span></code></pre></div></div>
<p>关闭顺序扫描强制选择索引扫描，并非最优计划，执行更慢。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">                                                        QUERY PLAN</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--------------------------------------------------------------------------------------------------------------------------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Index Scan using idx_t1_c1c2 on t1  (cost=0.42..18773.42 rows=100 width=16) (actual time=1.846..100.758 rows=90 loops=1)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Index Cond: (c2 = 100)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Planning Time: 0.147 ms</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Execution Time: 100.806 ms</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">(4 rows)</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="postgresql-18-执行计划选择使用索引扫描可以看出跳跃式扫描执行效率提升幅度非常大">PostgreSQL 18 执行计划选择使用索引扫描，可以看出跳跃式扫描执行效率提升幅度非常大<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#postgresql-18-%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92%E9%80%89%E6%8B%A9%E4%BD%BF%E7%94%A8%E7%B4%A2%E5%BC%95%E6%89%AB%E6%8F%8F%E5%8F%AF%E4%BB%A5%E7%9C%8B%E5%87%BA%E8%B7%B3%E8%B7%83%E5%BC%8F%E6%89%AB%E6%8F%8F%E6%89%A7%E8%A1%8C%E6%95%88%E7%8E%87%E6%8F%90%E5%8D%87%E5%B9%85%E5%BA%A6%E9%9D%9E%E5%B8%B8%E5%A4%A7" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">                                                        QUERY PLAN</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---------------------------------------------------------------------------------------------------------------------------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Index Scan using idx_t1_c1c2 on t1  (cost=0.42..3900.84 rows=100 width=16) (actual time=0.225..11.464 rows=90.00 loops=1)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Index Cond: (c2 = 100)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Index Searches: 1002</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Buffers: shared hit=3096</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Planning Time: 0.141 ms</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Execution Time: 11.522 ms</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">(6 rows)</span><br></span></code></pre></div></div>
<p>关闭索引扫描和位图扫描强制选择顺序扫描。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">postgres=# EXPLAIN ANALYZE SELECT * FROM t1 WHERE c2=100;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                                      QUERY PLAN</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">----------------------------------------------------------------------------------------------------------------------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Gather  (cost=1000.00..12986.33 rows=100 width=16) (actual time=1.486..86.881 rows=90.00 loops=1)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Workers Planned: 2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Workers Launched: 2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Buffers: shared hit=6768</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   -&gt;  Parallel Seq Scan on t1  (cost=0.00..11976.33 rows=42 width=16) (actual time=2.758..78.712 rows=30.00 loops=3)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         Filter: (c2 = 100)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         Rows Removed by Filter: 333303</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         Buffers: shared hit=6768</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Planning Time: 0.141 ms</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Execution Time: 86.926 ms</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">(10 rows)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="使用注意">使用注意<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>跳跃式扫描目前只能支持等值比较条件。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="小结-1">小结<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%B0%8F%E7%BB%93-1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>PostgreSQL 18 的索引跳跃式扫描，使得多列 BTREE 索引能够被那些仅对第二个或之后的索引列进行等值引用的查询使用，大幅减少索引扫描需要访问的条目，使其效率得到明显提升。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="三-虚拟生成列存储与计算的-灵活平衡">三、 虚拟生成列：存储与计算的 “灵活平衡”<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E4%B8%89-%E8%99%9A%E6%8B%9F%E7%94%9F%E6%88%90%E5%88%97%E5%AD%98%E5%82%A8%E4%B8%8E%E8%AE%A1%E7%AE%97%E7%9A%84-%E7%81%B5%E6%B4%BB%E5%B9%B3%E8%A1%A1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>PostgreSQL 18 开发体验相关的特性，聚焦于简化开发流程、提升代码灵活性，让开发者更高效地利用 PostgreSQL 能力。</p>
<p>IvorySQL 数据库长期致力于 Oracle 特性兼容，其中包含了一项虚拟列的语法兼容：</p>
<p><code>column [datatype][generated always] AS (column_expression)[VIRTUAL]</code></p>
<p>这次 PostgreSQL 18 终于也带来了虚拟列功能。虚拟列是一种不存储数据的表列，其值在查询时通过动态计算得出。与存储列相比，虚拟列节省了列存储空间，查询虚拟列值时通过计算虚拟列表达式的值作为该列的值。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="基本语法">基本语法<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%9F%BA%E6%9C%AC%E8%AF%AD%E6%B3%95" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>PostgreSQL 18 中虚拟列的语法和存储列的语法相似，新增加关键字<strong>VIRTUAL</strong>，当省略 STORED 和 VIRTUAL 关键字时默认为虚拟列。其语法如下所示：</p>
<p><code>GENERATED ALWAYS AS ( generation_expr ) [ STORED | VIRTUAL ]</code></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="虚拟列用例">虚拟列用例<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E8%99%9A%E6%8B%9F%E5%88%97%E7%94%A8%E4%BE%8B" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>虚拟列的标识是在列的限制条件中表示的，通过虚拟列的限制语法标识列为虚拟列，以下为虚拟列表的创建、查询和新增虚拟列：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 创建包含虚拟列的表，其中price_with_tax为虚拟列</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> products </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    id </span><span class="token keyword" style="color:#00009f">SERIAL</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name </span><span class="token keyword" style="color:#00009f">TEXT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">NOT</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    price </span><span class="token keyword" style="color:#00009f">NUMERIC</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">NOT</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tax_rate </span><span class="token keyword" style="color:#00009f">NUMERIC</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">DEFAULT</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.20</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    price_with_tax </span><span class="token keyword" style="color:#00009f">NUMERIC</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> GENERATED ALWAYS </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">price </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> tax_rate</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> VIRTUAL</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 插入数据</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> products </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> price</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tax_rate</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'Laptop'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000.00</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.20</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 查询数据（虚拟列自动计算）</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> price</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tax_rate</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> price_with_tax </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> products</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">  price  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> tax_rate </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> price_with_tax</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">--------+---------+----------+----------------</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Laptop </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000.00</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">     </span><span class="token number" style="color:#36acaa">0.20</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">        </span><span class="token number" style="color:#36acaa">1200.00</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">row</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">--为表添加虚拟列</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">ALTER</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> products </span><span class="token keyword" style="color:#00009f">ADD</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">COLUMN</span><span class="token plain"> selling_price </span><span class="token keyword" style="color:#00009f">NUMERIC</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">GENERATED ALWAYS </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    price </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> tax_rate</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> virtual</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="实现原理浅析">实现原理浅析<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E6%B5%85%E6%9E%90" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="虚拟列的创建">虚拟列的创建<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E8%99%9A%E6%8B%9F%E5%88%97%E7%9A%84%E5%88%9B%E5%BB%BA" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<p>创建的表中虚拟列的存储方式和普通列的存储方式类似，其列信息都存储在 pg_attribute 系统表中，其中 attgenerated 列存储生成列信息，如果该列的值为's'，表示该列为存储列。PostgreSQL 18 新增的虚拟列在该字段中的标识符为'v'，并且将虚拟列的表达式存储于 pg_attrdef 系统表中。</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">--查看虚拟列信息，其attgenerated为v表示该列为虚拟列</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">postgres</span><span class="token operator" style="color:#393A34">=</span><span class="token comment" style="color:#999988;font-style:italic"># select * from pg_attribute where attname='price_with_tax';</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"> RECORD </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">]</span><span class="token comment" style="color:#999988;font-style:italic">--+---------------</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attrelid       </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16388</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attname        </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> price_with_tax</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">atttypid       </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1700</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attlen         </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attnum         </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">atttypmod      </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">655366</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attndims       </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attbyval       </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> f</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attalign       </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> i</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attstorage     </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> m</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attcompression </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attnotnull     </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> f</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">atthasdef      </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> t</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">atthasmissing  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> f</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attidentity    </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attgenerated   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> v</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attisdropped   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> f</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attislocal     </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> t</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attinhcount    </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attcollation   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attstattarget  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attacl         </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attoptions     </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attfdwoptions  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attmissingval  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">--查看虚拟列表达式存储，可以看到以下表达式为虚拟列表达式</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">postgres</span><span class="token operator" style="color:#393A34">=</span><span class="token comment" style="color:#999988;font-style:italic"># select pg_get_expr(adbin, adrelid) from pg_attrdef where adnum = 5;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             pg_get_expr</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-------------------------------------</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">price </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">::</span><span class="token keyword" style="color:#00009f">numeric</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> tax_rate</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">row</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="虚拟列的插入或更新">虚拟列的插入或更新<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E8%99%9A%E6%8B%9F%E5%88%97%E7%9A%84%E6%8F%92%E5%85%A5%E6%88%96%E6%9B%B4%E6%96%B0" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<p>由于虚拟列的数据不占据存储空间，所以任何指定更新或插入虚拟列的操作都将被限制。</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">--指定插入虚拟列</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> products </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> price</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tax_rate</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> price_with_tax</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'Laptop'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000.00</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.20</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ERROR:  cannot </span><span class="token keyword" style="color:#00009f">insert</span><span class="token plain"> a non</span><span class="token operator" style="color:#393A34">-</span><span class="token keyword" style="color:#00009f">DEFAULT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">value</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">into</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">column</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"price_with_tax"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">DETAIL:  </span><span class="token keyword" style="color:#00009f">Column</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"price_with_tax"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">is</span><span class="token plain"> a generated </span><span class="token keyword" style="color:#00009f">column</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">--指定更新虚拟列</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">update</span><span class="token plain"> products </span><span class="token keyword" style="color:#00009f">set</span><span class="token plain"> price_with_tax </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">where</span><span class="token plain"> name </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Laptop'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ERROR:  </span><span class="token keyword" style="color:#00009f">column</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"price_with_tax"</span><span class="token plain"> can only be updated </span><span class="token keyword" style="color:#00009f">to</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">DEFAULT</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">DETAIL:  </span><span class="token keyword" style="color:#00009f">Column</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"price_with_tax"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">is</span><span class="token plain"> a generated </span><span class="token keyword" style="color:#00009f">column</span><span class="token punctuation" style="color:#393A34">.</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="虚拟列的查询">虚拟列的查询<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E8%99%9A%E6%8B%9F%E5%88%97%E7%9A%84%E6%9F%A5%E8%AF%A2" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<p>PostgreSQL 18 中查询虚拟列的实现是在生成执行计划阶段完成。在逻辑重写优化阶段，判断查询的范围表中是否包含虚拟列，如果包含虚拟列，则将该虚拟列的表达式从 pg_attrdef 中获取出来并替换原虚拟列名。这样查询虚拟列的值就相当于计算其表达式的值，即 <code>select price_with_tax</code> 相当于 <code>select (price \* ('1'::numeric + tax_rate)) as price_with_tax</code>。可以看到以下虚拟列被替换成了其表达式：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">postgres</span><span class="token operator" style="color:#393A34">=</span><span class="token comment" style="color:#999988;font-style:italic"># explain verbose SELECT name, price, tax_rate, price_with_tax</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> products</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                              QUERY </span><span class="token keyword" style="color:#00009f">PLAN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">----------------------------------------------------------------------</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Seq Scan </span><span class="token keyword" style="color:#00009f">on</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">public</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">products  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cost</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.00</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.23</span><span class="token number" style="color:#36acaa">.12</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">750</span><span class="token plain"> width</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">76</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Output: name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> price</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tax_rate</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">price </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'1'</span><span class="token plain">::</span><span class="token keyword" style="color:#00009f">numeric</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> tax_rate</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">2</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="适用场景">适用场景<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E9%80%82%E7%94%A8%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class="">当考虑存储空间时，可以使用虚拟列，因为虚拟列不占用磁盘空间。</li>
<li class="">当列的值需要根据依赖的列变化而变化时，需要使用虚拟列。因为虚拟列的值是动态获取的。</li>
<li class="">当虚拟列表达式简单时，可以使用虚拟列。因为查询虚拟列需消耗 CPU 资源，表达式复杂会消耗太多 CPU 资源。</li>
<li class="">因为 Oracle 中有虚拟列功能，更加方便 Oracle 的虚拟列迁移至 PostgreSQL 中。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="待完善部分">待完善部分<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%BE%85%E5%AE%8C%E5%96%84%E9%83%A8%E5%88%86" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>一些功能目前尚不支持，但可能会作为增量功能在后续的版本中添加：</p>
<ul>
<li class="">在虚拟列上创建索引或使用虚拟列。</li>
<li class="">虚拟列上也没有唯一约束。</li>
<li class="">虚拟列上的扩展统计信息。</li>
<li class="">虚拟列上的外键约束。</li>
<li class="">虚拟列上的非空约束（支持检查约束）。</li>
<li class="">ALTER TABLE / DROP EXPRESSION。</li>
<li class="">虚拟列不能具有域类型。</li>
<li class="">逻辑复制不支持虚拟列。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="小结-2">小结<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%B0%8F%E7%BB%93-2" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>虚拟列与普通列和存储列有着本质的不同，因为虚拟列的值不占磁盘空间，其获取值的方式也与普通列和存储列不同，普通列或存储列需要从磁盘获取数据，而虚拟列是通过动态计算获取虚拟列的值。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="四uuid-功能增强有序性与易用性提升">四、UUID 功能增强：有序性与易用性提升<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%9B%9Buuid-%E5%8A%9F%E8%83%BD%E5%A2%9E%E5%BC%BA%E6%9C%89%E5%BA%8F%E6%80%A7%E4%B8%8E%E6%98%93%E7%94%A8%E6%80%A7%E6%8F%90%E5%8D%87" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="传统-uuid-的无序性是其用作主键的主要痛点">传统 UUID 的无序性是其用作主键的主要痛点：<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E4%BC%A0%E7%BB%9F-uuid-%E7%9A%84%E6%97%A0%E5%BA%8F%E6%80%A7%E6%98%AF%E5%85%B6%E7%94%A8%E4%BD%9C%E4%B8%BB%E9%94%AE%E7%9A%84%E4%B8%BB%E8%A6%81%E7%97%9B%E7%82%B9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>传统 UUID（尤其是 v4）的完全随机性是其作为数据库主键的痛点：</p>
<ul>
<li class="">UUID 随机生成，插入位置不确定，导致索引树频繁分裂和重组，大幅降低写入性能。</li>
<li class="">破坏聚簇索引（如 InnoDB）的物理存储顺序，增加磁盘 I/O。</li>
<li class="">范围查询和排序效率低下，性能低下。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="uuidv7-的关键突破时间有序性架构设计">UUIDv7 的关键突破：时间有序性架构设计<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#uuidv7-%E7%9A%84%E5%85%B3%E9%94%AE%E7%AA%81%E7%A0%B4%E6%97%B6%E9%97%B4%E6%9C%89%E5%BA%8F%E6%80%A7%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>UUIDv7 通过在 UUID 的高位部分引入时间戳来解决生成 UUID 完全随机的问题，使新生成的 UUID 能够按照创建时间自然排序。这样，B 树索引可以像自增整数一样进行顺序插入，同时仍然保持 UUID 的全局唯一性和分布式生成优势。</p>
<p>该特性使 UUIDv7 作为主键具备以下突出优势：</p>
<ul>
<li class="">严格按照创建时间先后顺序递增。</li>
<li class="">减少索引碎片。</li>
<li class="">提高缓存命中率。</li>
<li class="">适合高并发插入和高效查询的场景。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="uuidv7-的结构设计">UUIDv7 的结构设计<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#uuidv7-%E7%9A%84%E7%BB%93%E6%9E%84%E8%AE%BE%E8%AE%A1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250926-uuidv7-5ddf263b41da1fb9be96b0f53d963b16.png" width="1093" height="245" class="img_ev3q"></p>
<table><thead><tr><th>字段</th><th>位数</th><th>说明</th></tr></thead><tbody><tr><td>毫秒级 Unix 时间戳</td><td>48 位</td><td>Unix 时间戳（毫秒）</td></tr><tr><td>亚毫秒级时间戳分数（用于额外排序）</td><td>12 位</td><td>时间戳的微秒精度扩展</td></tr><tr><td>随机数</td><td>62 位</td><td>随机数或计数器</td></tr><tr><td>版本号</td><td>4 位</td><td>固定为 0111（v7）</td></tr><tr><td>变体</td><td>2 位</td><td>固定为 10（RFC 4122）</td></tr></tbody></table>
<p><strong>设计关键点解析：</strong></p>
<ul>
<li class=""><strong>高精度时间前缀(48 位):</strong> 精确到毫秒的 Unix 时间戳，确保 ID 严格按时间递增（需 NTP 时钟同步）。</li>
<li class=""><strong>尾部随机位(62 位):</strong> 保证分布式唯一性，避免 v1 版本的 MAC 地址泄漏风险。</li>
</ul>
<p><strong>有序性如何解决性能问题？</strong></p>
<ul>
<li class=""><strong>B-Tree 索引优化:</strong> 新生成的 UUIDv7 总是大于之前的值，因此被追加到索引尾部，避免中间节点分裂。</li>
<li class=""><strong>缓冲池友好:</strong> 顺序写入使新记录集中在少数数据页。当页写满时，数据库只需分配新页追加，减少旧页淘汰与磁盘 I/O。</li>
<li class=""><strong>范围查询加速:</strong> 时间有序性使 WHERE id &gt; '2025-06-01' 可转化为时间戳范围过滤，大幅降低扫描范围。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="如何在-postgresql18-中应用-uuidv7">如何在 PostgreSQL18 中应用 UUIDv7<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%A6%82%E4%BD%95%E5%9C%A8-postgresql18-%E4%B8%AD%E5%BA%94%E7%94%A8-uuidv7" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>PostgreSQL 18 引入了多个新函数来支持 UUIDv7，方便生成、操作和提取 UUID 信息。</p>
<ol>
<li class=""><code>uuidv7()</code>函数：用于生成新的 UUIDv7 值</li>
</ol>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 使用当前时间戳生成 UUIDv7</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> uuidv7</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 输出示例: 0197f96c-b278-7f64-a32f-dae3cabe1ff0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 生成 1 小时前的 UUIDv7</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> uuidv7</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">INTERVAL</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'-1 hour'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 生成 30 分钟后的 UUIDv7</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> uuidv7</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">INTERVAL</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'30 minutes'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<ol start="2">
<li class=""><code>uuidv4()</code>函数：作为已有函数 <code>gen_random_uuid()</code> 的别名 ，便于和 uuidv7 一起使用</li>
</ol>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 两者等价</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> gen_random_uuid</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> uuidv4</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<ol start="3">
<li class=""><code>uuid_extract_timestamp()</code>函数 : 该函数现在支持 UUIDv7（原本只支持 UUIDv1）</li>
</ol>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 从 UUIDv7 提取时间戳</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> uuid_extract_timestamp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">uuidv7</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 示例输出: 2025-09-18 12:20:49.409+00</span><br></span></code></pre></div></div>
<ol start="4">
<li class=""><code>uuid_extract_version()</code>函数：用于检测 UUID 的版本：</li>
</ol>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 检查 UUID 版本</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> uuid_extract_version</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">uuidv7</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">-- 返回 7</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> uuid_extract_version</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">uuidv4</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">-- 返回 4</span><br></span></code></pre></div></div>
<p>PostgreSQL 数据库中使用 UUIDv7 作为主键：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">--创建带 UUIDv7 主键的表</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> users </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    id UUID </span><span class="token keyword" style="color:#00009f">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">KEY</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">DEFAULT</span><span class="token plain"> uuidv7</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    username </span><span class="token keyword" style="color:#00009f">VARCHAR</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">50</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">UNIQUE</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">NOT</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    email </span><span class="token keyword" style="color:#00009f">VARCHAR</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">NOT</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    created_at </span><span class="token keyword" style="color:#00009f">TIMESTAMP</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">WITH</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TIME</span><span class="token plain"> ZONE </span><span class="token keyword" style="color:#00009f">DEFAULT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">CURRENT_TIMESTAMP</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>这样，每条新记录的 <code>id</code> 都会自动分配一个按时间戳排序的 UUID。</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">--插入数据</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> users </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">username</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> email</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'alice'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'alice@example.com'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> users </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">username</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> email</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'bob'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'bob@example.com'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 按 UUID 时间顺序查看</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> username</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> uuid_extract_timestamp</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> uuid_timestamp</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> users</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">ORDER</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">BY</span><span class="token plain"> id</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="性能优势">性能优势：<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E6%80%A7%E8%83%BD%E4%BC%98%E5%8A%BF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ol>
<li class="">UUIDv7 的时间戳顺序能显著减少页分裂和缓存失效，有效提升 B 树索引效率。</li>
</ol>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">--创建性能测试表</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> performance_test </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    id_v4 UUID </span><span class="token keyword" style="color:#00009f">DEFAULT</span><span class="token plain"> uuidv4</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    id_v7 UUID </span><span class="token keyword" style="color:#00009f">DEFAULT</span><span class="token plain"> uuidv7</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">data</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TEXT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">DEFAULT</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'sample data'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">--使用UUIDv7作为索引</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INDEX</span><span class="token plain"> idx_v4 </span><span class="token keyword" style="color:#00009f">ON</span><span class="token plain"> performance_test </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">id_v4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INDEX</span><span class="token plain"> idx_v7 </span><span class="token keyword" style="color:#00009f">ON</span><span class="token plain"> performance_test </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">id_v7</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>批量插入后，你可以用 <code>pg_statio_user_indexes</code> 查看索引命中情况，UUIDv7 通常表现更优。</p>
<ol start="2">
<li class="">UUIDv7 自带时间排序，大部分场景下显著提升排序性能。</li>
</ol>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 利用 UUIDv7 自然排序</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> id_v7</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">data</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> performance_test</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">ORDER</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">BY</span><span class="token plain"> id_v7</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">LIMIT</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>相比 UUIDv4 的随机顺序，UUIDv7 查询结果按创建顺序返回，更直观。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="uuidv7-最佳实践">UUIDv7 最佳实践<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#uuidv7-%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250926-uuidv7-2-5dc7700627ddfee6c6ae0d2e1c26eabe.png" width="1062" height="389" class="img_ev3q"></p>
<p>适合使用 UUIDv7 的场景：</p>
<ul>
<li class="">
<p><strong>多租户应用</strong>：可用 UUIDv7 做主键，并为 <code>(tenant_id, id)</code> 创建复合索引，既保持唯一性又能按时间排序。</p>
</li>
<li class="">
<p><strong>分布式系统</strong>：多个服务可独立生成 UUIDv7，并且在全局范围内仍能保持时间顺序。</p>
</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="限制与注意事项">限制与注意事项<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E9%99%90%E5%88%B6%E4%B8%8E%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class=""><strong>依赖系统时钟</strong>：需启用 NTP 等时间同步机制，避免时钟漂移。</li>
<li class=""><strong>时间戳精度</strong>：UUIDv7 以毫秒为单位，在同一毫秒内生成多个 UUID，顺序可能无法完全反映真实创建顺序，但仍保持唯一性。</li>
<li class=""><strong>迁移规划</strong>：从 UUIDv4 迁移到 UUIDv7 时，需要检查应用逻辑、索引和外部依赖。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="小结-3">小结<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%B0%8F%E7%BB%93-3" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>PostgreSQL 18 对 UUIDv7 的支持，解决了 UUID 作为主键的性能瓶颈。UUIDv7 在保持全局唯一性的同时，具备类似自增整数的顺序性，使 B 树插入更高效，查询更快。</p>
<p>对于需要分布式、高并发和高性能的现代应用，UUIDv7 提供了一种兼顾唯一性和性能的实用解决方案。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="五explain-增强直观呈现执行细节">五、EXPLAIN 增强：直观呈现执行细节<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E4%BA%94explain-%E5%A2%9E%E5%BC%BA%E7%9B%B4%E8%A7%82%E5%91%88%E7%8E%B0%E6%89%A7%E8%A1%8C%E7%BB%86%E8%8A%82" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>PostgreSQL 18 对 EXPLAIN 命令进行了重大升级，通过提供更丰富、更直观的执行计划信息，让数据库开发者和 DBA 能够更加轻松地进行查询性能分析与优化。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="自动缓冲区分析">自动缓冲区分析<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E8%87%AA%E5%8A%A8%E7%BC%93%E5%86%B2%E5%8C%BA%E5%88%86%E6%9E%90" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>EXPLAIN ANALYZE 现在默认包含 BUFFER 统计信息，无需手动添加 BUFFERS 选项：</p>
<ul>
<li class=""><strong>共享命中(Shared Hits)</strong>：显示从缓存中读取的数据块数量，反映内存使用效率。</li>
<li class=""><strong>共享读取(Shared Reads)</strong>：标识必须从磁盘读取的数据块，帮助识别 I/O 瓶颈。</li>
<li class=""><strong>共享脏块(Shared Dirtied)</strong>：针对数据修改操作，显示被更改的块数量。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="精细化索引监控">精细化索引监控<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E7%B2%BE%E7%BB%86%E5%8C%96%E7%B4%A2%E5%BC%95%E7%9B%91%E6%8E%A7" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>新增索引扫描次数统计，让开发者能够精确了解索引使用效率：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 示例输出显示索引使用情况</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">Index</span><span class="token plain"> Scan </span><span class="token keyword" style="color:#00009f">using</span><span class="token plain"> orders_pkey </span><span class="token keyword" style="color:#00009f">on</span><span class="token plain"> orders</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">Index</span><span class="token plain"> Searches: </span><span class="token number" style="color:#36acaa">1</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">-- 明确显示索引查找次数</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Buffers: shared hit</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">read</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="增强的统计信息">增强的统计信息<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%A2%9E%E5%BC%BA%E7%9A%84%E7%BB%9F%E8%AE%A1%E4%BF%A1%E6%81%AF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ul>
<li class="">支持小数行（ fractional row counts），提供更精确的行数估计。</li>
<li class="">为 Material、Window Aggregate、CTE 节点输出内存和磁盘使用详情。</li>
<li class="">在窗口函数中显示详细的参数信息。</li>
<li class="">为 Parallel Bitmap Heap Scan 显示 worker 缓存统计。</li>
<li class="">输出禁用节点。</li>
<li class="">输出 WAL 缓冲区信息。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="基础查询分析">基础查询分析<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%9F%BA%E7%A1%80%E6%9F%A5%E8%AF%A2%E5%88%86%E6%9E%90" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">-- 创建测试表</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> orders </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    order_id </span><span class="token keyword" style="color:#00009f">SERIAL</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    customer_id </span><span class="token keyword" style="color:#00009f">INTEGER</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">NOT</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    order_date </span><span class="token keyword" style="color:#00009f">DATE</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">NOT</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    total_amount </span><span class="token keyword" style="color:#00009f">DECIMAL</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">NOT</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INDEX</span><span class="token plain"> idx_orders_customer_id </span><span class="token keyword" style="color:#00009f">ON</span><span class="token plain"> orders</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">customer_id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 插入测试数据</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> orders </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">customer_id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> order_date</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> total_amount</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n </span><span class="token operator" style="color:#393A34">%</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">CURRENT_DATE</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n </span><span class="token operator" style="color:#393A34">%</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">365</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">50</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">950</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">::</span><span class="token keyword" style="color:#00009f">decimal</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> generate_series</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">50000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> n</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-- 查看增强的执行计划</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">EXPLAIN</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">ANALYZE</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token punctuation" style="color:#393A34">,</span><span class="token function" style="color:#d73a49">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">total_amount</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">OVER</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">PARTITION</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">BY</span><span class="token plain"> customer_id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> orders </span><span class="token keyword" style="color:#00009f">WHERE</span><span class="token plain"> order_id</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token number" style="color:#36acaa">49900</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>执行计划：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">QUERY </span><span class="token keyword" style="color:#00009f">PLAN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">----------------------------------------------------------------------------------------------------------------------------------------</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> WindowAgg  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cost</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">13.46</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.15</span><span class="token number" style="color:#36acaa">.04</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">99</span><span class="token plain"> width</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">50</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">actual </span><span class="token keyword" style="color:#00009f">time</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.630</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.0</span><span class="token number" style="color:#36acaa">.745</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">100.00</span><span class="token plain"> loops</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Window: w1 </span><span class="token keyword" style="color:#00009f">AS</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">PARTITION</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">BY</span><span class="token plain"> customer_id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Storage: Memory  Maximum Storage: </span><span class="token number" style="color:#36acaa">17</span><span class="token plain">kB</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Buffers: shared hit</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">5</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">read</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">  Sort  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cost</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">13.30</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.13</span><span class="token number" style="color:#36acaa">.55</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">99</span><span class="token plain"> width</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">18</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">actual </span><span class="token keyword" style="color:#00009f">time</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.307</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.0</span><span class="token number" style="color:#36acaa">.333</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">100.00</span><span class="token plain"> loops</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         Sort </span><span class="token keyword" style="color:#00009f">Key</span><span class="token plain">: customer_id</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         Sort Method: quicksort  Memory: </span><span class="token number" style="color:#36acaa">28</span><span class="token plain">kB</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         Buffers: shared hit</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">5</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">read</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">  </span><span class="token keyword" style="color:#00009f">Index</span><span class="token plain"> Scan </span><span class="token keyword" style="color:#00009f">using</span><span class="token plain"> orders_pkey </span><span class="token keyword" style="color:#00009f">on</span><span class="token plain"> orders  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cost</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.29</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.10</span><span class="token number" style="color:#36acaa">.02</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">99</span><span class="token plain"> width</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">18</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">actual </span><span class="token keyword" style="color:#00009f">time</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.089</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.0</span><span class="token number" style="color:#36acaa">.174</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">100.00</span><span class="token plain"> loops</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               </span><span class="token keyword" style="color:#00009f">Index</span><span class="token plain"> Cond: </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">order_id </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">49900</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               </span><span class="token keyword" style="color:#00009f">Index</span><span class="token plain"> Searches: </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               Buffers: shared hit</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">read</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Planning:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Buffers: shared hit</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">64</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">read</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">22</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Planning </span><span class="token keyword" style="color:#00009f">Time</span><span class="token plain">: </span><span class="token number" style="color:#36acaa">2.080</span><span class="token plain"> ms</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Execution </span><span class="token keyword" style="color:#00009f">Time</span><span class="token plain">: </span><span class="token number" style="color:#36acaa">1.343</span><span class="token plain"> ms</span><br></span></code></pre></div></div>
<p>执行计划输出洞察：</p>
<ul>
<li class="">缓冲区使用情况（缓存命中 vs 磁盘读取）：生成执行计划时从缓存中访问了 64 个共享缓冲区，从磁盘中读取了 22 个缓冲区，执行时从缓冲区访问了 5 个共享缓冲区，从磁盘读取了 2 个缓冲区。</li>
<li class="">索引效率统计：执行了 1 次 orders_pkey 索引扫描，并从缓存中访问了 2 个共享缓冲区，清晰地显示了索引的使用效率。</li>
<li class="">窗口函数内存使用详情：使用的 17kB 磁盘空间。</li>
<li class="">精确的行统计信息。</li>
<li class="">窗口函数的详细参数。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="wal-日志分析">WAL 日志分析<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#wal-%E6%97%A5%E5%BF%97%E5%88%86%E6%9E%90" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">EXPLAIN</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">ANALYZE</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> WAL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> orders </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">customer_id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> order_date</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> total_amount</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n </span><span class="token operator" style="color:#393A34">%</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">CURRENT_DATE</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n </span><span class="token operator" style="color:#393A34">%</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">365</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">50</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">950</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">::</span><span class="token keyword" style="color:#00009f">decimal</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> generate_series</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">50000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> n</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>执行计划：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">QUERY </span><span class="token keyword" style="color:#00009f">PLAN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">---------------------------------------------------------------------------------------------------------------------------------------------</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">Insert</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">on</span><span class="token plain"> orders  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cost</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.00</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.2000</span><span class="token number" style="color:#36acaa">.00</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> width</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">actual </span><span class="token keyword" style="color:#00009f">time</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">767.116</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.767</span><span class="token number" style="color:#36acaa">.118</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.00</span><span class="token plain"> loops</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Buffers: shared hit</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">299156</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">read</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token plain"> dirtied</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">500</span><span class="token plain"> written</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">501</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   WAL: records</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">152158</span><span class="token plain"> bytes</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">10427828</span><span class="token plain"> buffers </span><span class="token keyword" style="color:#00009f">full</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">139</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">  Subquery Scan </span><span class="token keyword" style="color:#00009f">on</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"*SELECT*"</span><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cost</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.00</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.2000</span><span class="token number" style="color:#36acaa">.00</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">50000</span><span class="token plain"> width</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">28</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">actual </span><span class="token keyword" style="color:#00009f">time</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">5.742</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.336</span><span class="token number" style="color:#36acaa">.699</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">50000.00</span><span class="token plain"> loops</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         Buffers: shared hit</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">50013</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         WAL: records</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1516</span><span class="token plain"> bytes</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">150084</span><span class="token plain"> buffers </span><span class="token keyword" style="color:#00009f">full</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">  </span><span class="token keyword" style="color:#00009f">Function</span><span class="token plain"> Scan </span><span class="token keyword" style="color:#00009f">on</span><span class="token plain"> generate_series n  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cost</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.00</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.1750</span><span class="token number" style="color:#36acaa">.00</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">50000</span><span class="token plain"> width</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">24</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">actual </span><span class="token keyword" style="color:#00009f">time</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">5.460</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.227</span><span class="token number" style="color:#36acaa">.650</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">50000.00</span><span class="token plain"> loops</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Planning </span><span class="token keyword" style="color:#00009f">Time</span><span class="token plain">: </span><span class="token number" style="color:#36acaa">0.114</span><span class="token plain"> ms</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Execution </span><span class="token keyword" style="color:#00009f">Time</span><span class="token plain">: </span><span class="token number" style="color:#36acaa">767.179</span><span class="token plain"> ms</span><br></span></code></pre></div></div>
<p>WAL 统计：</p>
<ul>
<li class="">监控写入负载的日志生成量：WAL 缓冲区生成 1516 条日志，共 150084 个字节的数据。</li>
<li class="">诊断写入性能瓶颈：缓冲区被写满了 2 次。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="并行查询优化">并行查询优化<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%B9%B6%E8%A1%8C%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%8C%96" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">EXPLAIN</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">ANALYZE</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> orders </span><span class="token keyword" style="color:#00009f">WHERE</span><span class="token plain"> customer_id </span><span class="token operator" style="color:#393A34">IN</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">6</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>执行计划：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">QUERY </span><span class="token keyword" style="color:#00009f">PLAN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">-------------------------------------------------------------------------------------------------------------------------------------------------------</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Gather  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cost</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2752.40</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.10357</span><span class="token number" style="color:#36acaa">.99</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">327855</span><span class="token plain"> width</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">18</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">actual </span><span class="token keyword" style="color:#00009f">time</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">22.375</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.121</span><span class="token number" style="color:#36acaa">.296</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">330000.00</span><span class="token plain"> loops</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Workers Planned: </span><span class="token number" style="color:#36acaa">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Workers Launched: </span><span class="token number" style="color:#36acaa">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Buffers: shared hit</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">3810</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">  Parallel Bitmap Heap Scan </span><span class="token keyword" style="color:#00009f">on</span><span class="token plain"> orders  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cost</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">2751.40</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.10029</span><span class="token number" style="color:#36acaa">.13</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">136606</span><span class="token plain"> width</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">18</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">actual </span><span class="token keyword" style="color:#00009f">time</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">12.868</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.88</span><span class="token number" style="color:#36acaa">.329</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">110000.00</span><span class="token plain"> loops</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         Recheck Cond: </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">customer_id </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">ANY</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'{1,2,3,4,5,6}'</span><span class="token plain">::</span><span class="token keyword" style="color:#00009f">integer</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token keyword" style="color:#00009f">Rows</span><span class="token plain"> Removed </span><span class="token keyword" style="color:#00009f">by</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">Index</span><span class="token plain"> Recheck: </span><span class="token number" style="color:#36acaa">53967</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         Heap Blocks: exact</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">170</span><span class="token plain"> lossy</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">566</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         Buffers: shared hit</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">3810</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         Worker </span><span class="token number" style="color:#36acaa">0</span><span class="token plain">:  Heap Blocks: exact</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">387</span><span class="token plain"> lossy</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">957</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         Worker </span><span class="token number" style="color:#36acaa">1</span><span class="token plain">:  Heap Blocks: exact</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">369</span><span class="token plain"> lossy</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1055</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">  Bitmap </span><span class="token keyword" style="color:#00009f">Index</span><span class="token plain"> Scan </span><span class="token keyword" style="color:#00009f">on</span><span class="token plain"> idx_orders_customer_id  </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cost</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.00</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.2669</span><span class="token number" style="color:#36acaa">.44</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">327855</span><span class="token plain"> width</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">actual </span><span class="token keyword" style="color:#00009f">time</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">21.219</span><span class="token punctuation" style="color:#393A34">.</span><span class="token number" style="color:#36acaa">.21</span><span class="token number" style="color:#36acaa">.220</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">330000.00</span><span class="token plain"> loops</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               </span><span class="token keyword" style="color:#00009f">Index</span><span class="token plain"> Cond: </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">customer_id </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">ANY</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'{1,2,3,4,5,6}'</span><span class="token plain">::</span><span class="token keyword" style="color:#00009f">integer</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               </span><span class="token keyword" style="color:#00009f">Index</span><span class="token plain"> Searches: </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               Buffers: shared hit</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">266</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Planning:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Buffers: shared hit</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">30</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Planning </span><span class="token keyword" style="color:#00009f">Time</span><span class="token plain">: </span><span class="token number" style="color:#36acaa">0.510</span><span class="token plain"> ms</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Execution </span><span class="token keyword" style="color:#00009f">Time</span><span class="token plain">: </span><span class="token number" style="color:#36acaa">158.523</span><span class="token plain"> ms</span><br></span></code></pre></div></div>
<p>并行执行效率洞察：</p>
<ul>
<li class="">每个工作进程的缓存统计详情：worker 0 命中 387 个精确块和 957 个有损块，worker 1 命中 369 个精确块和 1055 个有损块。</li>
<li class="">精确块与有损块分析：出现有损块说明可能 work_mem 太小导致 bitmap 无法精准定位元组。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="技术优势与价值">技术优势与价值<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E6%8A%80%E6%9C%AF%E4%BC%98%E5%8A%BF%E4%B8%8E%E4%BB%B7%E5%80%BC" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="即时性能诊断">即时性能诊断<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%8D%B3%E6%97%B6%E6%80%A7%E8%83%BD%E8%AF%8A%E6%96%AD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<ul>
<li class=""><strong>降低门槛</strong>：自动化的缓冲区统计让初学者快速识别 I/O 问题。</li>
<li class=""><strong>深度洞察</strong>：为专家级用户提供更细粒度的性能数据。</li>
<li class=""><strong>全面覆盖</strong>：单条命令获取执行计划、缓存使用、索引效率等多维信息。</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="优化指导">优化指导<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E4%BC%98%E5%8C%96%E6%8C%87%E5%AF%BC" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<ul>
<li class=""><strong>索引优化</strong>：通过精确的索引使用统计，避免过度索引或索引不足。</li>
<li class=""><strong>内存调优</strong>：根据有损块出现频率指导 work_mem 参数调整</li>
<li class=""><strong>查询重写</strong>：基于详细的执行成本数据优化 SQL 语句结构</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="功能未来展望">功能未来展望<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%8A%9F%E8%83%BD%E6%9C%AA%E6%9D%A5%E5%B1%95%E6%9C%9B" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>尽管 PostgreSQL 18 的 EXPLAIN 增强带来了显著改进，但仍有一些方面可以进一步完善：</p>
<ol>
<li class=""><strong>输出可读性</strong>：随着信息量的增加，输出变得更加复杂，可能需要更好的格式化或可视化工具支持</li>
<li class=""><strong>历史对比</strong>：缺乏直接与历史执行计划对比的内置机制，使得性能回归分析仍需依赖外部工具</li>
<li class=""><strong>阈值警报</strong>：没有内置机制对异常值（如异常高的缓冲区读取）发出警告，需要手动分析</li>
<li class=""><strong>执行计划可视化</strong>：文本形式的输出在复杂查询中仍难以直观理解，需要第三方工具补充</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="小结-4">小结<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%B0%8F%E7%BB%93-4" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>PostgreSQL 18 的 EXPLAIN 增强代表了数据库可观测性的重大进步。通过自动化收集关键性能指标并提供更深入的执行洞察，它显著降低了查询优化的门槛，同时为经验丰富的 DBA 提供了更强大的分析能力。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="六oauth-20-认证支持筑牢数据防护壁垒">六、OAuth 2.0 认证支持：筑牢数据防护壁垒<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%85%ADoauth-20-%E8%AE%A4%E8%AF%81%E6%94%AF%E6%8C%81%E7%AD%91%E7%89%A2%E6%95%B0%E6%8D%AE%E9%98%B2%E6%8A%A4%E5%A3%81%E5%9E%92" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>在安全方面，IvorySQL 致力于加入多种国密认证功能来保障数据安全。而这次 PostgreSQL18 在身份认证方面继续加强，引入对 OAuth 2 的支持。这是一种开放标准的授权协议，用于授权一个应用程序或服务访问用户在另一个应用程序中的资源，而无需提供用户名和密码。</p>
<p>该特性主要包含以下几个核心要素：</p>
<ul>
<li class=""><strong>OAuth2 验证器框架</strong>：提供了一个可扩展的框架，使 PostgreSQL 能够与 OAuth 2.0 提供程序集成。PostgreSQL 本身不实现具体的令牌验证算法（如 JWT 验证），而是将这项工作委托给一个外部共享库 (<code>*.so</code> 文件)。</li>
<li class=""><strong>客户端认证支持</strong>：libpq（PostgreSQL 的 C 客户端库）现在支持 OAuth 2.0 认证流程。</li>
<li class=""><strong>自定义验证逻辑</strong>：通过回调机制允许实现自定义的令牌验证和用户映射逻辑。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="配置-oauth-认证方式">配置 OAuth 认证方式<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E9%85%8D%E7%BD%AE-oauth-%E8%AE%A4%E8%AF%81%E6%96%B9%E5%BC%8F" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="服务端配置">服务端配置<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E6%9C%8D%E5%8A%A1%E7%AB%AF%E9%85%8D%E7%BD%AE" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<ol>
<li class="">
<p>选择 OAuth 认证的方式与瀚高数据库选择国密认证的方式类似，需要通过在 pg_hba.conf 文件中指定 METHOD 为 oauth，开启 OAuth 认证。</p>
<p>同时 OPTIONS 必须指定 issuer 和 scope 参数，除此之外还有几个可选参数：validator、map、delegate_ident_mapping，以下是一个最简配置示例：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">local all test oauth issuer="http://127.0.0.1:9000" scope="openid postgre"</span><br></span></code></pre></div></div>
</li>
<li class="">
<p>指定外部 OAuth 验证器，在 postgresql.conf 文件中配置新提供的 oauth_validator_libraries 参数，配置内容为 OAuth 验证器提供的库文件。</p>
</li>
</ol>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="客户端配置">客户端配置<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%85%8D%E7%BD%AE" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<p>客户端在连接时需要指定以下连接参数从而实现连接：</p>
<ul>
<li class="">oauth_issuer：必要参数，HTTPS URL，是授权服务器的颁发者标识符。</li>
<li class="">oauth_client_id：必要参数，由授权服务器颁发的 OAuth 2.0 客户端标识符。</li>
<li class="">oauth_client_secret：可选参数，访问 OAuth 授权服务器时要使用的客户端密码。</li>
<li class="">oauth_scope：可选参数，发送到授权服务器的访问请求的范围，指定为 OAuth 范围标识符的空格分隔列表。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="认证实现原理">认证实现原理<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E8%AE%A4%E8%AF%81%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>oauth 整体认证流程大致如下图所示：</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250926-oauth-97f62144108afc05d4145c50d8b8cec1.png" width="766" height="931" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="客户端libpq">客户端（libpq）<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%AE%A2%E6%88%B7%E7%AB%AFlibpq" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<p>PostgreSQL 实现了一个<strong>非阻塞的、基于状态机的异步网络客户端</strong>。状态机包含 <code>OAUTH_STEP_INIT</code>、<code>OAUTH_STEP_DISCOVERY</code>、<code>OAUTH_STEP_DEVICE_AUTHORIZATION</code>、<code>OAUTH_STEP_TOKEN_REQUEST</code>、<code>OAUTH_STEP_WAIT_INTERVAL</code> 这几个状态。其核心原理包含以下几个部分：</p>
<ul>
<li class=""><strong>DISCOVERY</strong>：客户端从用户请求中获取授权服务器元信息。</li>
<li class=""><strong>DEVICE_AUTHORIZATION</strong>：客户端向授权服务器发送请求，授权服务器返回 device_code 和 verification_uri。客户端输出信息"Visit xxxxxx and enter the code: xxxxxx"，提示用户进行操作。</li>
<li class=""><strong>TOKEN_REQUEST 和 WAIT_INTERVAL</strong>：轮询访问授权服务器，直到用户完成授权，授权服务器返回 access_token 给客户端。</li>
<li class="">将获取到的 <code>access_token</code> 设置到连接对象中。<code>libpq</code> 会将它作为密码发送给 PostgreSQL 服务器，服务器端的 OAuth 验证器会负责校验这个令牌。</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="服务端">服务端<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E6%9C%8D%E5%8A%A1%E7%AB%AF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<p>以下 PostgreSQL 服务端处理 OAuth 认证流程，同样通过状态机实现，但要比客户端简单得多，总共分为 <code>OAUTH_STATE_INIT</code>、<code>OAUTH_STATE_ERROR</code>、<code>OAUTH_STATE_FINISHED</code> 三个状态。以下是核心步骤：</p>
<ul>
<li class="">首先解析客户端发送的消息，该消息格式遵循 RFC 7628 第 3.1 节部分。</li>
<li class="">从客户端消息中提取出纯粹的 Bearer Token，并验证其格式（是否为合法的 Base64 字符串）。</li>
<li class="">将提取出的令牌传递给验证器模块进行实质性的验证。<!-- -->
<ul>
<li class="">验证成功：状态转为 <code>OAUTH_STATE_FINISHED</code>，返回 <code>PG_SASL_EXCHANGE_SUCCESS</code>。进行建立连接的后续操作。</li>
<li class="">验证失败：生成一个符合 RFC 7628 第 3.2.2 节的 JSON 错误响应，告知客户端所需的 <code>scope</code> 和到哪里获取令牌。状态转为 <code>OAUTH_STATE_ERROR</code>，并返回 <code>PG_SASL_EXCHANGE_CONTINUE</code>，等待客户端发送最终的 <code>KVSEP</code> 来结束失败的握手。</li>
</ul>
</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="外部验证器">外部验证器<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%A4%96%E9%83%A8%E9%AA%8C%E8%AF%81%E5%99%A8" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h4>
<p>外部验证器通常需要处理以下事项：</p>
<ul>
<li class="">令牌验证：可以通过在线验证和本地验证两种方式，由验证器自行决定。在线验证下验证器通常将令牌发送到授权服务器专门的 <code>Introspection Endpoint</code>，授权服务器会返回一个 JSON 响应，告知令牌是否有效。本地验证则需要验证器内部实现一套验证流程，本地验证令牌的签名和有效期。本地验证的好处在于能够快速响应，但缺点是无法实时检测令牌撤销。</li>
<li class="">身份映射：在验证通过后，验证器需要提取令牌中的唯一用户标识，并转换为数据库可理解的身份标识，也就是数据库用户。</li>
<li class="">连接决策：如果令牌处于有效期并且存在相应的数据库用户映射关系，则以该用户的身份创建会话连接。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="优缺点剖析">优缺点剖析<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E4%BC%98%E7%BC%BA%E7%82%B9%E5%89%96%E6%9E%90" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p><strong>优点：</strong></p>
<ol>
<li class="">OAuth2 提供了现代、标准化的身份验证机制，提高了安全性。通过 OAuth2 认证，规避了传统密码认证在数据传输过程中暴露密码导致的安全风险。</li>
<li class="">简化了数据库用户管理，支持统一的身份策略和访问控制，提高了管理效率。</li>
</ol>
<p><strong>缺点：</strong></p>
<ol>
<li class="">相较于传统的密码认证，实现更加复杂，需要额外的配置和维护工作，包括 OAuth2 提供程序的设置和管理，提高了运维的复杂度。比如瀚高数据库的国密认证功能，同样在保障口令安全的同时仅需要通过非常简单的配置即可使用。</li>
<li class="">依赖于外部 OAuth 提供程序的可用性和可靠性，若 OAuth 提供程序出现问题，可能会影响数据库访问。</li>
<li class="">每次连接可能需要额外的网络请求来验证令牌，可能会增加连接建立的时间，特别是在高并发场景下。</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="小结-5">小结<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E5%B0%8F%E7%BB%93-5" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>PostgreSQL 18 引入的 OAuth2 支持是一个重要的安全增强功能，它允许组织使用现代的身份验证机制来保护数据库访问。通过提供灵活的验证器框架和回调机制，PostgreSQL 18 可以适应各种 OAuth2 部署场景和业务需求。</p>
<p>虽然 OAuth2 认证增加了系统的复杂性，但它提供了显著的安全优势，特别是在集中式身份管理和单点登录方面。对于希望采用现代安全最佳实践的组织来说，这是一个值得考虑的新功能。OAuth 认证并不与已有的认证方式冲突，在不希望进行繁琐的配置时，仍然可以选择类似于瀚高数据库提供的国密认证等方式。</p>
<p>在实施 OAuth2 认证时，组织应该仔细评估其需求、现有基础设施和技术能力，以确保成功部署和运维。同时应该考虑到性能、可用性和兼容性等因素，以提供最佳的用户体验和系统可靠性。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="总结">总结<a href="https://ivorysql.org/zh-cn/blog/analysis-of-pg-18-key-new-features#%E6%80%BB%E7%BB%93" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>PostgreSQL 18 凭借六大核心特性实现了性能、功能与安全性的全方位升级：</p>
<ul>
<li class="">异步 I/O 突破了同步阻塞瓶颈，提升读取密集型场景的吞吐量。</li>
<li class="">跳跃式扫描让多列 B 树索引在非前导列查询中发挥高效作用。</li>
<li class="">虚拟生成列在存储与计算间找到了灵活平衡，优化了开发体验。</li>
<li class="">UUIDv7 解决了传统 UUID 无序性带来的性能痛点，兼顾唯一性与顺序性。</li>
<li class="">EXPLAIN 增强为查询优化提供了更直观、细致的执行洞察。</li>
<li class="">OAuth 2.0 认证则为数据安全筑牢了现代防护屏障。</li>
</ul>
<p>这些特性不仅满足了当前数据库在高性能、高并发、易开发、强安全等方面的需求，也为未来在跨平台适配、功能扩展等方向奠定了坚实基础，进一步巩固了 PostgreSQL 在开源数据库领域的领先地位，为各类应用场景提供了更强大、更灵活的技术支撑。</p>]]></content:encoded>
            <category>IvorySQL</category>
            <category>Database</category>
            <category>UUIDv7</category>
            <category>PostgreSQL</category>
            <category>I/O</category>
            <category>OAuth</category>
        </item>
        <item>
            <title><![CDATA[IvorySQL-WASM：免安装的数据库探索之旅]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/ivorysql-wasm</link>
            <guid>https://ivorySQL.org/zh-cn/blog/ivorysql-wasm</guid>
            <pubDate>Tue, 27 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[简介]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="简介">简介<a href="https://ivorysql.org/zh-cn/blog/ivorysql-wasm#%E7%AE%80%E4%BB%8B" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>为了降低社区用户的使用门槛，提升使用体验，IvorySQL 社区特别推出了 IvorySQL-WASM 项目，帮助用户快速在线 Demo。</p>
<p>IvorySQL-WASM 基于开源的 Postgres-WASM 框架开发。它允许用户直接在网页浏览器中体验 IvorySQL，无需本地安装或复杂部署。通过结合 WebAssembly 和虚拟化技术，IvorySQL-WASM 提供了一种简单、轻量且易于访问的方式，让开发者、测试人员和数据库爱好者能够轻松探索 IvorySQL 的强大功能。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="技术分析">技术分析<a href="https://ivorysql.org/zh-cn/blog/ivorysql-wasm#%E6%8A%80%E6%9C%AF%E5%88%86%E6%9E%90" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL-WASM 核心技术：</p>
<ul>
<li class="">
<p><strong>V86</strong>：x86 模拟器，用于模拟虚拟操作系统所需的硬件环境，包括 CPU、内存、VGA、BIOS 和串口等组件。</p>
</li>
<li class="">
<p><strong>BuildRoot</strong>：用于构建在 V86 上运行的精简 Linux 系统镜像，集成 IvorySQL 4.4 服务及其稳定运行环境。</p>
</li>
<li class="">
<p><strong>WebAssembly (WASM)</strong>：一种低级的类汇编二进制指令格式，通过 <a href="https://emscripten.org/" target="_blank" rel="noopener noreferrer" title="Emscripten" class="">Emscripten</a> 等工具将 C/C++ 代码编译为 WASM 模块，从而将现有的 C 库生态系统移植到 Web 平台。WASM 具有紧凑的二进制格式和接近原生的运行性能，是 IvorySQL-WASM 项目运行数据库服务的核心技术。</p>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="快速部署">快速部署<a href="https://ivorysql.org/zh-cn/blog/ivorysql-wasm#%E5%BF%AB%E9%80%9F%E9%83%A8%E7%BD%B2" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>开发者若想在本地部署 IvorySQL-WASM 项目，可参考以下步骤：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="linux-环境">Linux 环境<a href="https://ivorysql.org/zh-cn/blog/ivorysql-wasm#linux-%E7%8E%AF%E5%A2%83" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ol>
<li class="">准备运行依赖工具：Node.js 和 npm。</li>
<li class="">克隆项目：<code>git clone git@github.com:IvorySQL/ivorysql-wasm.git</code></li>
<li class="">进入 <code>package/runtime</code> 目录，启动 http 服务：<code>npx serve</code></li>
<li class="">在浏览器访问：<code>http://server_ip:3000</code></li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="windows-环境">Windows 环境<a href="https://ivorysql.org/zh-cn/blog/ivorysql-wasm#windows-%E7%8E%AF%E5%A2%83" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ol>
<li class="">下载并安装 <a href="https://nodejs.org/dist/v22.15.0/node-v22.15.0-x64.msi" target="_blank" rel="noopener noreferrer" title="Node.js" class="">Node.js</a>。</li>
<li class="">在 PowerShell 的相应目录下执行 <code>npx.cmd serve</code>，启动 http 服务器。</li>
</ol>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/w-1-5a8c60395b5b08e5d9435b0cd67c747b.png" width="692" height="303" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="体验">体验<a href="https://ivorysql.org/zh-cn/blog/ivorysql-wasm#%E4%BD%93%E9%AA%8C" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>通过访问 <code>http://192.168.31.186:3000/</code>，用户可以进行 IvorySQL 在线体验，例如创建 <code>t1</code> 表格并列出表格。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/w-2-73fbcfe398872626e3e16b39767cf183.png" width="1874" height="772" class="img_ev3q"></p>
<p>运行精简 Linux 虚拟机时，浏览器需先下载相关镜像系统文件（约 35MB）和状态快照（15MB）。因此，首次打开页面可能需要等待几秒至二十几秒，具体时长取决于 http 服务位于内网还是外网环境。状态快照用于快速加载预配置的数据库。</p>
<p>通过任务管理器可以观察到，IvorySQL 在浏览器中的运行对 CPU 和内存的占用较为理想。</p>
<p><img decoding="async" loading="lazy" alt="img" src="data:image/png;base64,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" width="958" height="65" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="注意事项">注意事项<a href="https://ivorysql.org/zh-cn/blog/ivorysql-wasm#%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ol>
<li class="">
<p>数据库存储在内存中，刷新页面后数据库将被清空，系统恢复到初始状态。</p>
</li>
<li class="">
<p>IvorySQL 采用双端口设计，默认连接端口为 1521。若需从 5432 端口登录，请在 shell 终端退出 psql，然后执行：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">psql -U postgres -p 5432 -d postgres</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/w-4-05b8390bab87d0d912f731e29bf50a31.png" width="803" height="263" class="img_ev3q"></p>
</li>
<li class="">
<p>如果用户想保存当前的数据库怎么办？请点击“Save state to file”，浏览器会自动产生并下载 <code>v86state.bin</code> 文件；再次使用保持的数据库时，点击页面“Restore from file”，上传 <code>v86state.bin</code> 文件即可。</p>
</li>
<li class="">
<p>V86 系统最小内存配置 128 MB，即当前为配置。</p>
</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="开发者可以改动源码满足实际需求">开发者可以改动源码，满足实际需求。<a href="https://ivorysql.org/zh-cn/blog/ivorysql-wasm#%E5%BC%80%E5%8F%91%E8%80%85%E5%8F%AF%E4%BB%A5%E6%94%B9%E5%8A%A8%E6%BA%90%E7%A0%81%E6%BB%A1%E8%B6%B3%E5%AE%9E%E9%99%85%E9%9C%80%E6%B1%82" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ol>
<li class="">BuildRoot 部分请参考 <code>package/buildroot/README.md</code></li>
<li class="">V86 部分请参考<a href="https://github.com/copy/v86/releases/tag/latest" target="_blank" rel="noopener noreferrer" class="">https://github.com/copy/v86/releases/tag/latest</a></li>
<li class="">Web 页面部分请参考 <code>package/runtime/README.md</code></li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="社区版即将发布">社区版即将发布<a href="https://ivorysql.org/zh-cn/blog/ivorysql-wasm#%E7%A4%BE%E5%8C%BA%E7%89%88%E5%8D%B3%E5%B0%86%E5%8F%91%E5%B8%83" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL-WASM 社区版将会伴随 IvorySQL 4.5 一起发布，详情请关注 <a href="https://www.ivorysql.org/zh-cn/" target="_blank" rel="noopener noreferrer" class="">IvorySQL 官网</a>信息。</p>]]></content:encoded>
            <category>IvorySQL</category>
            <category>Database</category>
            <category>Oracle Compatible</category>
            <category>PostgreSQL</category>
            <category>WasmEdge</category>
        </item>
        <item>
            <title><![CDATA[IvorySQL 增量备份与合并增量备份功能解析]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features</link>
            <guid>https://ivorySQL.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features</guid>
            <pubDate>Thu, 20 Mar 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[1. 概述]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-概述">1. 概述<a href="https://ivorysql.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features#1-%E6%A6%82%E8%BF%B0" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL v4 引入了块级增量备份和增量备份合并功能，旨在优化数据库备份与恢复流程。通过 <code>pg_basebackup</code> 工具支持增量备份，显著降低了存储需求和备份时间。同时，<code>pg_combinebackup</code> 工具能够将多个增量备份合并为单个完整备份，提升了数据恢复的灵活性和效率。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-特性介绍">2. 特性介绍<a href="https://ivorysql.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features#2-%E7%89%B9%E6%80%A7%E4%BB%8B%E7%BB%8D" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL 的增量备份通过记录每个 checkpoint 周期内的数据变更，确保仅备份自上次备份以来发生变化的数据块。这种机制不仅减少了所需的存储空间，也缩短了备份过程中的 I/O 操作时间。此外，<code>pg_combinebackup</code> 工具支持将多个增量备份合并为一个完整备份，使得在数据恢复时不再需要逐个应用增量备份，大大简化了恢复流程。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250320-1-8f4120529e6e3b72a1eb39447241ed2e.png" width="1267" height="558" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-启用增量备份特性">2.1 启用增量备份特性<a href="https://ivorysql.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features#21-%E5%90%AF%E7%94%A8%E5%A2%9E%E9%87%8F%E5%A4%87%E4%BB%BD%E7%89%B9%E6%80%A7" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>为启用增量备份功能，首先需要在数据库中配置相关参数并重新加载配置文件：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">ALTER</span><span class="token plain"> SYSTEM </span><span class="token keyword" style="color:#00009f">SET</span><span class="token plain"> summarize_wal </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">ON</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> pg_reload_conf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>接着，创建测试表并插入初始数据，以便后续进行备份操作：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> sample_table</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">t </span><span class="token keyword" style="color:#00009f">INT</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> sample_table </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> sample_table</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250320-2-ebb15bf5ff6e1a025f31ab176d56a71f.png" width="297" height="103" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-执行全量备份">2.2 执行全量备份<a href="https://ivorysql.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features#22-%E6%89%A7%E8%A1%8C%E5%85%A8%E9%87%8F%E5%A4%87%E4%BB%BD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>使用 <code>pg_basebackup</code> 工具执行全量备份，并生成包含 <code>backup_manifest</code> 的备份文件，以便后续增量备份的基础：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pg_basebackup -Fp -D /backup/\$(date +%Y-%m-%d*%H%M%S-FULL)</span><br></span></code></pre></div></div>
<p>此命令将全量备份数据保存至指定目录，并记录当前的备份状态。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-进行增量备份">2.3 进行增量备份<a href="https://ivorysql.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features#23-%E8%BF%9B%E8%A1%8C%E5%A2%9E%E9%87%8F%E5%A4%87%E4%BB%BD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>（1）在对数据进行修改后，执行增量备份以记录自上次全量备份或增量备份以来的变更。</p>
<p>修改表数据并执行第一次增量备份：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">UPDATE</span><span class="token plain"> sample_table </span><span class="token keyword" style="color:#00009f">SET</span><span class="token plain"> t </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> sample_table</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250320-3-f6122485af0a5a07e2e015eccf77e216.png" width="291" height="138" class="img_ev3q"></p>
<p>执行增量备份：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pg_basebackup -Fp -D /backup/\$(date +%Y-%m-%d*%H%M%S-INCREMENTAL) -i /backup/2025-02-20_161530-FULL/backup_manifest</span><br></span></code></pre></div></div>
<p>（2）再次修改表数据并执行第二次增量备份。</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">UPDATE</span><span class="token plain"> sample_table </span><span class="token keyword" style="color:#00009f">SET</span><span class="token plain"> t </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> sample_table</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250320-4-3a56abb51193113ec5438b524ed93a9e.png" width="314" height="142" class="img_ev3q"></p>
<p>然后执行第二次增量备份：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pg_basebackup -Fp -D /backup/\$(date +%Y-%m-%d*%H%M%S-INCREMENTAL) -i /backup/2025-02-20_161808-INCREMENTAL/backup_manifest</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250320-5-7427bc75793302a6177d8145139b9c8b.png" width="599" height="105" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="24-合并增量备份">2.4 合并增量备份<a href="https://ivorysql.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features#24-%E5%90%88%E5%B9%B6%E5%A2%9E%E9%87%8F%E5%A4%87%E4%BB%BD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>合并全量备份和多个增量备份，创建新的完整备份，以便后续的恢复操作：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cd /backup</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pg_combinebackup 2025-02-20_161530-FULL 2025-02-20_161808-INCREMENTAL 2025-02-20_162115-INCREMENTAL -o 2025-02-20_66666-FULL</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250320-6-5c830f7a20815ce3c1e0e6c1abd1d5a5.png" width="605" height="128" class="img_ev3q"></p>
<blockquote>
<p>注意事项：全量备份必须在参数列表的首位，增量备份需要按时间顺序排列，以确保数据恢复的完整性与一致性。</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="25-恢复合并后的备份">2.5 恢复合并后的备份<a href="https://ivorysql.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features#25-%E6%81%A2%E5%A4%8D%E5%90%88%E5%B9%B6%E5%90%8E%E7%9A%84%E5%A4%87%E4%BB%BD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>启动数据库并验证数据的完整性：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pg_ctl </span><span class="token keyword" style="color:#00009f">start</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain">D </span><span class="token operator" style="color:#393A34">/</span><span class="token keyword" style="color:#00009f">backup</span><span class="token operator" style="color:#393A34">/</span><span class="token number" style="color:#36acaa">2025</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">02</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">20</span><span class="token plain">_66666</span><span class="token operator" style="color:#393A34">-</span><span class="token keyword" style="color:#00009f">FULL</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain">o </span><span class="token string" style="color:#e3116c">'-p 5435'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> sample_table</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250320-7-fa0cc137940024161bb3c75547a3636d.png" width="1059" height="532" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="26-合并指定增量备份">2.6 合并指定增量备份<a href="https://ivorysql.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features#26-%E5%90%88%E5%B9%B6%E6%8C%87%E5%AE%9A%E5%A2%9E%E9%87%8F%E5%A4%87%E4%BB%BD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>如需恢复到特定的增量备份状态，可以选择合并到某个中间增量备份：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pg_combinebackup </span><span class="token number" style="color:#36acaa">2025</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">02</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">20</span><span class="token plain">_161530</span><span class="token operator" style="color:#393A34">-</span><span class="token keyword" style="color:#00009f">FULL</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2025</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">02</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">20</span><span class="token plain">_161808</span><span class="token operator" style="color:#393A34">-</span><span class="token plain">INCREMENTAL </span><span class="token operator" style="color:#393A34">-</span><span class="token plain">o </span><span class="token number" style="color:#36acaa">2025</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">02</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">20</span><span class="token plain">_77777</span><span class="token operator" style="color:#393A34">-</span><span class="token keyword" style="color:#00009f">FULL</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250320-8-09c567f682acc20fe5cba35c19538d6e.png" width="594" height="140" class="img_ev3q"></p>
<p>然后启动数据库并验证数据：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pg_ctl </span><span class="token keyword" style="color:#00009f">start</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain">D </span><span class="token operator" style="color:#393A34">/</span><span class="token keyword" style="color:#00009f">backup</span><span class="token operator" style="color:#393A34">/</span><span class="token number" style="color:#36acaa">2025</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">02</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">20</span><span class="token plain">_77777</span><span class="token operator" style="color:#393A34">-</span><span class="token keyword" style="color:#00009f">FULL</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain">o </span><span class="token string" style="color:#e3116c">'-p 5436'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> sample_table</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250320-9-3b1553fe5925fcf96099b34bda2e6cb5.png" width="1049" height="536" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-注意事项">3. 注意事项<a href="https://ivorysql.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features#3-%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="备份顺序">备份顺序<a href="https://ivorysql.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features#%E5%A4%87%E4%BB%BD%E9%A1%BA%E5%BA%8F" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>确保全量备份在合并命令的首位，增量备份按照时间顺序排列，以避免合并过程中的数据不一致性问题。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="增量备份存储管理">增量备份存储管理<a href="https://ivorysql.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features#%E5%A2%9E%E9%87%8F%E5%A4%87%E4%BB%BD%E5%AD%98%E5%82%A8%E7%AE%A1%E7%90%86" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>虽然增量备份减少了存储空间的需求，但恢复操作依赖于完整的增量备份链，任何增量备份的丢失将直接导致恢复失败。因此，建议定期检查和合并增量备份，以确保备份链的完整性和可靠性。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="性能监控">性能监控<a href="https://ivorysql.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features#%E6%80%A7%E8%83%BD%E7%9B%91%E6%8E%A7" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>在执行增量备份和合并操作时，监控数据库的性能，确保操作对系统性能的影响在可接受范围内，避免在高负载时段进行备份操作。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-总结">4. 总结<a href="https://ivorysql.org/zh-cn/blog/ivorysql-incremental-backup-and-merge-features#4-%E6%80%BB%E7%BB%93" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL 提供的块级增量备份与 <code>pg_combinebackup</code> 工具显著提升了数据库的备份与恢复效率。通过只备份变更的数据块，增量备份在减小存储占用和缩短备份时间方面展现出卓越性能，而合并增量备份功能则简化了恢复流程，提升了操作的便捷性。</p>
<p>这些先进的特性使得 IvorySQL 成为大规模数据库和对数据恢复要求严格的环境的理想选择，能够提供灵活而高效的备份与恢复解决方案，确保数据的安全与可用性。</p>]]></content:encoded>
            <category>IvorySQL</category>
            <category>Database</category>
            <category>Oracle Compatible</category>
            <category>PostgreSQL</category>
            <category>Incremental Backup</category>
        </item>
        <item>
            <title><![CDATA[IvorySQL v4 逻辑复制槽同步功能解析]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/ivorysql-logical-replication-slot</link>
            <guid>https://ivorySQL.org/zh-cn/blog/ivorysql-logical-replication-slot</guid>
            <pubDate>Wed, 05 Mar 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[IvorySQL v4 基于 PostgreSQL 17，引入了逻辑复制槽同步至热备份数据库的功能。这一改进有效解决了旧版本中主数据库与备份数据库切换后逻辑复制中断的问题。对于那些追求数据高可用性和业务连续性的数据库来说，这无疑是一个重大的利好消息。它不仅提升了系统的整体稳定性，还确保了在故障发生时，数据复制过程能够无缝继续，从而最大程度地降低了业务中断的可能性。]]></description>
            <content:encoded><![CDATA[<p>IvorySQL v4 基于 PostgreSQL 17，引入了逻辑复制槽同步至热备份数据库的功能。这一改进有效解决了旧版本中主数据库与备份数据库切换后逻辑复制中断的问题。对于那些追求数据高可用性和业务连续性的数据库来说，这无疑是一个重大的利好消息。它不仅提升了系统的整体稳定性，还确保了在故障发生时，数据复制过程能够无缝继续，从而最大程度地降低了业务中断的可能性。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250305-1-b477c3d95d833e1ecff18533f5055692.png" width="1186" height="960" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="复制槽同步机制">复制槽同步机制<a href="https://ivorysql.org/zh-cn/blog/ivorysql-logical-replication-slot#%E5%A4%8D%E5%88%B6%E6%A7%BD%E5%90%8C%E6%AD%A5%E6%9C%BA%E5%88%B6" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="前提条件">前提条件<a href="https://ivorysql.org/zh-cn/blog/ivorysql-logical-replication-slot#%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ol>
<li class=""><strong>物理复制槽</strong>：主数据库与备份数据库之间必须存在物理复制槽，作为逻辑复制槽同步的基础，确保数据在主备之间有效传输。。</li>
<li class=""><strong>配置参数</strong>：<!-- -->
<ul>
<li class="">在备份数据库上配置 <code>primary_slot_name</code> 参数，并在使用 <code>pg_basebackup</code> 工具时指定复制槽。这一配置步骤确保了备份数据库能够正确接收主数据库数据。</li>
<li class="">启用备份数据库的 <code>hot_standby_feedback</code> 功能，确保其能接收并反馈 WAL 日志。这一功能的启用保证了备份数据库在接收数据时的活跃性和反馈机制的完整性。</li>
<li class="">在 <code>primary_conninfo</code> 参数中指定有效的数据库名称（<code>dbname</code>）。这一配置确保了复制过程中的目标数据库是明确且正确的。</li>
</ul>
</li>
<li class=""><strong>推荐配置</strong>：在主数据库上配置 <code>standby_slot_names</code> 参数以保持复制槽同步的一致性。这一配置有助于维护主备数据库间复制槽的一致状态，从而提高复制过程的可靠性。</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="实现方式">实现方式<a href="https://ivorysql.org/zh-cn/blog/ivorysql-logical-replication-slot#%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ol>
<li class="">
<p><strong>创建逻辑复制槽</strong>：
调用 <code>pg_create_logical_replication_slot</code> 函数时设置 <code>failover=true</code>，指定复制槽同步至备份数据库。这一设置至关重要，因为它决定了复制槽在主备切换时的行为。</p>
</li>
<li class="">
<p><strong>订阅配置</strong>：
使用 <code>CREATE SUBSCRIPTION</code> 语句时指定 <code>failover=true</code>，确保复制槽同步至备份数据库。这为数据库管理员提供了灵活的配置选项，使他们能够根据实际业务需求和环境特点定制复制策略。</p>
</li>
<li class="">
<p><strong>手动同步</strong>：
在备份数据库上执行 <code>pg_sync_replication_slots</code> 函数，手动同步复制槽。这种方法提供了一种即时同步的手段，特别适用于需要立即反映主数据库变更的场景。</p>
</li>
<li class="">
<p><strong>自动同步：</strong>
设置备份数据库的 <code>sync_replication_slots = on</code>，实现定期自动同步，无需重启数据库。这一自动化功能简化了数据库的维护工作，使管理员能够将更多精力投入到其他关键任务中。</p>
</li>
</ol>
<p>下图是配置及创建同步逻辑复制槽的流程图，详细的顺序讲解了如何配置同步逻辑复制槽基础参数，以及如何创建一个同步逻辑复制槽。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250305-2-e00b5bc5efe4913922963cdba55c9b26.png" width="1269" height="1126" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="函数与参数">函数与参数<a href="https://ivorysql.org/zh-cn/blog/ivorysql-logical-replication-slot#%E5%87%BD%E6%95%B0%E4%B8%8E%E5%8F%82%E6%95%B0" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<ol>
<li class=""><code>pg_create_logical_replication_slot</code></li>
</ol>
<ul>
<li class="">新增了 failover 参数，其默认值为 false。若设置为 true，则表示该复制槽需同步至备份数据库。</li>
<li class="">示例语法：<!-- -->
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> pg_create_logical_replication_slot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'test_slot'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'test_decoding'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
</li>
<li class="">这一函数的引入，为数据库管理员提供了更精细的控制手段，使得逻辑复制槽的管理更加灵活和高效。</li>
</ul>
<ol start="2">
<li class=""><code>pg_sync_replication_slots</code></li>
</ol>
<ul>
<li class="">该函数用于手动同步复制槽至备份数据库。</li>
<li class="">示例语法：<!-- -->
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> pg_sync_replication_slots</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
</li>
<li class="">通过这一函数，可以即时地将主数据库的变更同步到备份数据库，确保数据的一致性和完整性。</li>
</ul>
<ol start="3">
<li class=""><code>sync_replication_slots</code></li>
</ol>
<ul>
<li class="">在备份数据库上设置该参数为 on，可实现复制槽变更的定期自动同步，无需重启数据库。</li>
<li class="">这一自动化设置极大地减轻了数据库管理员的负担，使得复制槽的维护工作更加轻松和高效。</li>
</ul>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250305-3-e368dd57bce7eec33a1d91b5aba72fbf.png" width="402" height="735" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250305-4-438cb045e492559d95e6371d26f3efe3.png" width="387" height="391" class="img_ev3q"></p>
<p>如上图所示，为主库和备库的复制槽的状态，主库创建了逻辑复制槽 <code>test_slots2</code>，并定义属性 failover 为 true，开启了这个槽的故障转移功能，通过手动或自动的方式，可以将逻辑复制槽 <code>test_slots2</code> 的状态及信息同步至备库，当发生主备切换或者故障转移时，备库被提升为新主时，逻辑复制槽 <code>test_slots2</code> 的信息将会保留，从而保证订阅端间断、不丢失数据。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="操作步骤与验证">操作步骤与验证<a href="https://ivorysql.org/zh-cn/blog/ivorysql-logical-replication-slot#%E6%93%8D%E4%BD%9C%E6%AD%A5%E9%AA%A4%E4%B8%8E%E9%AA%8C%E8%AF%81" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="手动同步复制槽">手动同步复制槽<a href="https://ivorysql.org/zh-cn/blog/ivorysql-logical-replication-slot#%E6%89%8B%E5%8A%A8%E5%90%8C%E6%AD%A5%E5%A4%8D%E5%88%B6%E6%A7%BD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>搭建主数据库与备份数据库环境，确保主数据库和备份数据库的配置正确，包括物理复制槽和流复制用户。这一过程需要仔细规划和执行，以确保复制环境的稳定性和可靠性。</p>
<ol>
<li class="">创建复制槽：</li>
</ol>
<ul>
<li class="">执行<!-- -->
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> pg_create_logical_replication_slot</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'test_slot2'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'test_decoding'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
</li>
<li class="">这一操作是逻辑复制槽同步的起点，它为后续的数据复制奠定了基础。</li>
</ul>
<ol start="2">
<li class="">在备份数据库上手动同步复制槽：</li>
</ol>
<ul>
<li class="">执行<!-- -->
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> pg_sync_replication_slots</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
</li>
<li class="">这一操作确保了复制槽的变更能够及时反映到备份数据库上，从而保证了数据的同步性和一致性。</li>
</ul>
<ol start="3">
<li class="">验证复制槽同步：</li>
</ol>
<ul>
<li class="">在备份数据库上执行查询，以确保复制槽已同步至备份数据库。</li>
<li class="">执行<!-- -->
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> \</span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">FROM</span><span class="token plain"> pg_replication_slots</span><br></span></code></pre></div></div>
</li>
<li class="">这一验证步骤是确认复制槽同步成功的关键环节，它确保了复制过程的正确性和完整性。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="自动同步复制槽">自动同步复制槽<a href="https://ivorysql.org/zh-cn/blog/ivorysql-logical-replication-slot#%E8%87%AA%E5%8A%A8%E5%90%8C%E6%AD%A5%E5%A4%8D%E5%88%B6%E6%A7%BD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ol>
<li class="">在备份数据库上设置 <code>sync_replication_slots = on</code>：</li>
</ol>
<ul>
<li class="">执行<!-- -->
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">ALTER</span><span class="token plain"> SYSTEM </span><span class="token keyword" style="color:#00009f">SET</span><span class="token plain"> sync_replication_slots </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">on</span><br></span></code></pre></div></div>
</li>
<li class="">然后执行<!-- -->
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">SELECT</span><span class="token plain"> pg_reload_conf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
</li>
<li class="">这一自动化设置使得复制槽的同步工作更加简便和高效，管理员无需手动介入即可完成同步任务。</li>
</ul>
<ol start="2">
<li class="">
<p>验证自动同步：</p>
<p>在主数据库插入数据，观察备份数据库复制槽的变化，以确保主数据库复制槽的变更能够自动同步到备份数据库。这一验证步骤是确认自动同步功能正常工作的关键环节，它确保了复制过程的自动化和可靠性。</p>
</li>
</ol>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250305-5-6970523f5ffe31f36e0dbe1002dbc6a4.png" width="1029" height="298" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="模拟主数据库与备份数据库的切换">模拟主数据库与备份数据库的切换<a href="https://ivorysql.org/zh-cn/blog/ivorysql-logical-replication-slot#%E6%A8%A1%E6%8B%9F%E4%B8%BB%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%8E%E5%A4%87%E4%BB%BD%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E5%88%87%E6%8D%A2" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<ol>
<li class="">在备份数据库上执行主数据库提升操作：</li>
</ol>
<ul>
<li class="">模拟主数据库故障，将主数据库关机。</li>
<li class="">在备份数据库上执行<!-- -->
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pg_ctl </span><span class="token operator" style="color:#393A34">-</span><span class="token plain">D </span><span class="token keyword" style="color:#00009f">data</span><span class="token plain"> promote</span><br></span></code></pre></div></div>
</li>
<li class="">这一操作模拟了主数据库发生故障时的应急切换，是测试复制槽同步功能在实际故障场景中表现的重要步骤。</li>
</ul>
<ol start="2">
<li class="">
<p>在新的主数据库上继续进行逻辑复制：</p>
<p>在新的主数据库绑定 VIP，以确保逻辑复制能够继续进行。这一操作确保了在主数据库切换后，逻辑复制能够无缝地继续进行，从而保证了业务的连续性和数据的一致性。</p>
</li>
</ol>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250305-6-93a10935e1cfcbe01bd4281cefb0191d.png" width="1064" height="335" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250305-7-ff9ef06a8cf25189bfd6b3344337dc0a.png" width="1074" height="190" class="img_ev3q"></p>
<p>上图为切换后的新主，保留了原有的逻辑复制槽 <code>my_subscription</code> 和发布信息 <code>my_publication</code>。并且配置其他必要参数后，恢复新备库的逻辑复制槽同步能力。</p>
<p>订阅端根据 VIP 路由至新主数据库，继续使用同步的逻辑复制槽，保证逻辑复制的正常数据复制。</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/20250305-8-5a8ce2312c35d3a112e51651baf81824.png" width="1070" height="153" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="总结">总结<a href="https://ivorysql.org/zh-cn/blog/ivorysql-logical-replication-slot#%E6%80%BB%E7%BB%93" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>逻辑复制槽同步功能提升了 IvorySQL 在高可用性环境下的可靠性，解决了主数据库与备份数据库切换后逻辑复制槽丢失的问题。这一功能的引入，使得数据库面对主备切换时，保证逻辑复制槽的信息不丢失，确保了数据复制过程的连续性和一致性。</p>
<p>同步过程需满足以下条件：</p>
<ul>
<li class="">设置 <code>failover=true</code> 的复制槽；</li>
<li class="">主数据库与备份数据库间配置物理复制槽；</li>
<li class="">备份数据库启用 <code>hot_standby_feedback</code>；</li>
<li class="">备份数据库的 <code>primary_conninfo</code> 参数配置正确的数据库名称；</li>
<li class="">推荐配置 <code>standby_slot_names</code> 参数以确保一致性；</li>
<li class="">通过 <code>pg_sync_replication_slots</code> 或 <code>sync_replication_slots</code> 参数，备份数据库可以手动或自动同步复制槽，确保逻辑复制能够无缝切换。</li>
</ul>
<p>这一系列的措施和功能，共同构成了 IvorySQL 中逻辑复制槽同步功能的强大和灵活，为数据库管理员提供了强大的工具，以应对各种复杂和挑战性的数据复制场景。</p>]]></content:encoded>
            <category>IvorySQL</category>
            <category>Database</category>
            <category>Oracle Compatible</category>
            <category>PostgreSQL</category>
            <category>Logical Replication Slot</category>
            <category>逻辑复制槽</category>
        </item>
        <item>
            <title><![CDATA[IvorySQL 4.0 之 Invisible Column 功能解析]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/ivorysql-invisible-column</link>
            <guid>https://ivorySQL.org/zh-cn/blog/ivorysql-invisible-column</guid>
            <pubDate>Mon, 13 Jan 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[前言]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="前言">前言<a href="https://ivorysql.org/zh-cn/blog/ivorysql-invisible-column#%E5%89%8D%E8%A8%80" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>随着数据库应用场景的多样化，用户对数据管理的灵活性和隐私性提出了更高要求。IvorySQL 作为一款基于 PostgreSQL 并兼容 Oracle 的开源数据库，始终致力于在功能上保持领先和创新。在最新发布的 4.0 版本中，<strong>IvorySQL 新增了 Oracle 兼容特性 <a href="https://github.com/IvorySQL/IvorySQL/pull/679" target="_blank" rel="noopener noreferrer" class="">Invisible Column</a>（不可见列），这一功能由社区贡献者 <a href="https://github.com/imranzaheer612" target="_blank" rel="noopener noreferrer" class="">Imran Zaheer</a> 提供，体现了开源社区协作的力量</strong>。</p>
<p>Invisible Column 的引入，为开发者提供了在不影响现有应用的情况下动态调整数据库结构的新选择，进一步提升了 IvorySQL 在数据灵活性管理上的能力，为用户在数据库升级、兼容性优化等方面提供了更大的便利性。</p>
<p>本文将详细介绍这一特性的功能、使用场景以及使用方式。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="什么是-invisible-column">什么是 Invisible Column？<a href="https://ivorysql.org/zh-cn/blog/ivorysql-invisible-column#%E4%BB%80%E4%B9%88%E6%98%AF-invisible-column" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>在现代数据库开发中，列的可见性管理在一定程度上影响了应用程序的灵活性与迁移效率。Oracle 12c 提供了一项强大的功能：Invisible Column（不可见列）。这是一种隐藏数据列的特性，用于增强数据安全性和实现业务逻辑。这一功能为开发者提供了灵活性和控制能力，特别是在应用程序迁移或版本升级的场景中。</p>
<p>在 Oracle 中，Invisible Column 是指那些对大多数 SQL 查询和工具不可见的列。通过将列设置为不可见列：</p>
<ul>
<li class="">它不会出现在常规的 <code>SELECT * FROM</code> 查询结果中。</li>
<li class="">它不会在 <code>SQL*Plus</code> 或 <code>OCI</code> 的描述操作中显示。</li>
<li class="">它不会包含在基于 <code>%ROWTYPE</code> 属性的记录定义中。</li>
</ul>
<p>然而，不可见列仍然存在于表中，可以通过显式指定列名来访问或引用。另外，不可见列在使用时也有限制，要注意在外部表（External Tables）、聚簇表（Cluster Tables）、临时表（Temporary Tables）中无法使用不可见列。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="invisible-column-的应用场景">Invisible Column 的应用场景<a href="https://ivorysql.org/zh-cn/blog/ivorysql-invisible-column#invisible-column-%E7%9A%84%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-应用程序迁移">1. 应用程序迁移<a href="https://ivorysql.org/zh-cn/blog/ivorysql-invisible-column#1-%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E8%BF%81%E7%A7%BB" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>不可见列在应用程序迁移过程中非常有用。当我们向现有表中添加新列时，不可见列可以避免影响旧应用程序的功能。旧的应用程序不会察觉新列的存在，而新的应用程序可以显式引用这些列。从而使应用程序的在线迁移变得更加简单顺畅。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-敏感数据保护">2. 敏感数据保护<a href="https://ivorysql.org/zh-cn/blog/ivorysql-invisible-column#2-%E6%95%8F%E6%84%9F%E6%95%B0%E6%8D%AE%E4%BF%9D%E6%8A%A4" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>某些敏感数据可以通过不可见列存储，避免被大多数默认查询工具访问，从而降低意外暴露的风险。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-数据模型优化">3. 数据模型优化<a href="https://ivorysql.org/zh-cn/blog/ivorysql-invisible-column#3-%E6%95%B0%E6%8D%AE%E6%A8%A1%E5%9E%8B%E4%BC%98%E5%8C%96" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>在数据模型调整或调试过程中，可以临时将一些列设置为不可见列，确保它们不会影响常规查询，避免查询结果混淆。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="在-ivorysql-中使用-invisible-column">在 IvorySQL 中使用 Invisible Column<a href="https://ivorysql.org/zh-cn/blog/ivorysql-invisible-column#%E5%9C%A8-ivorysql-%E4%B8%AD%E4%BD%BF%E7%94%A8-invisible-column" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>Invisible Column 为 IvorySQL 4.0 版本中新增加的兼容特性，使用前请先确保您的版本为 4.0。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-创建不可见列">1. 创建不可见列<a href="https://ivorysql.org/zh-cn/blog/ivorysql-invisible-column#1-%E5%88%9B%E5%BB%BA%E4%B8%8D%E5%8F%AF%E8%A7%81%E5%88%97" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>可以在创建表时直接将列定义为不可见列：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">CREATE TABLE employees (</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">emp_id NUMBER,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">emp_name VARCHAR2(50),</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">emp_salary NUMBER INVISIBLE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">);</span><br></span></code></pre></div></div>
<p>在此示例中，<code>emp_salary</code> 是不可见列，对默认查询不可见：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">select*from employees ;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">emp_id | emp_name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--------+----------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">(0 rows)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-向不可见列插入数据">2. 向不可见列插入数据<a href="https://ivorysql.org/zh-cn/blog/ivorysql-invisible-column#2-%E5%90%91%E4%B8%8D%E5%8F%AF%E8%A7%81%E5%88%97%E6%8F%92%E5%85%A5%E6%95%B0%E6%8D%AE" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>在向表中插入数据时，可以通过显式指定列名的方式向不可见列插入数据：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">INSERT INTO employees(emp_id,emp_name,emp_salary) VALUES(1,'Jack',20000);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">INSERT 0 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">INSERT INTO employees(emp_id,emp_name,emp_salary) VALUES(2,'Lucy',30000);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">INSERT 0 1;</span><br></span></code></pre></div></div>
<p>不带命名列的插入不能包含不可见列：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">INSERT INTO employees VALUES(3,'Peter');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">INSERT 0 1</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-显示修改现有列为不可见列">3. 显示/修改现有列为不可见列<a href="https://ivorysql.org/zh-cn/blog/ivorysql-invisible-column#3-%E6%98%BE%E7%A4%BA%E4%BF%AE%E6%94%B9%E7%8E%B0%E6%9C%89%E5%88%97%E4%B8%BA%E4%B8%8D%E5%8F%AF%E8%A7%81%E5%88%97" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>通过 <code>VISIBLE</code> 关键字，可以将不可见列改回普通列：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ALTER TABLE employees MODIFY emp_salary VISIBLE;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ALTER TABLE</span><br></span></code></pre></div></div>
<p>如果需要将现有列设置为不可见列，可以使用 <code>INVISIBLE</code>：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ALTER TABLE employees MODIFY emp_salary INVISIBLE;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ALTER TABLE</span><br></span></code></pre></div></div>
<p>注意，不能将所有的列设置为不可见列。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-psql-d-元命令">4. psql \d 元命令<a href="https://ivorysql.org/zh-cn/blog/ivorysql-invisible-column#4-psql-d-%E5%85%83%E5%91%BD%E4%BB%A4" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>在 psql 中使用 <code>\d</code> 元命令时不会显示该表的不可见列信息：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">\d employees</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  Table "public.employees"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Column   |     Type     | Collation | Nullable | Default </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">------------+--------------+-----------+----------+---------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> emp_id     | number       |           |          | </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> emp_name   | varchar2(50) |           |          | </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> emp_salary | number       |           |          | </span><br></span></code></pre></div></div>
<p>可以使用含有更多表信息的 <code>\d+</code> 元命令查看该表的不可见列信息：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">\d+ employees</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                                   Table "public.employees"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   Column   |     Type     | Collation | Nullable | Default | Invisible | Storage  | Compression | Stats target | Description </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">------------+--------------+-----------+----------+---------+-----------+----------+-------------+--------------+-------------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> emp_id     | number       |           |          |         |           | main     |             |              | </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> emp_name   | varchar2(50) |           |          |         |           | extended |             |              | </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> emp_salary | number       |           |          |         | invisible | main     |             |              | </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Access method: heap</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-访问-invisible-column">5. 访问 Invisible Column<a href="https://ivorysql.org/zh-cn/blog/ivorysql-invisible-column#5-%E8%AE%BF%E9%97%AE-invisible-column" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>在使用 <code>SELECT*</code> 查询表数据时，不会显示不可见列的数据：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">SELECT * FROM employees ;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> emp_id | emp_name </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--------+----------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> 1      | Jack</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> 2      | Lucy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> 3      | Peter</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">(3 rows)</span><br></span></code></pre></div></div>
<p>虽然不可见列对默认查询不可见，但开发者仍然可以通过显式指定列名来访问它：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">SELECT emp_name,emp_salary FROM employees ;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> emp_name | emp_salary </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">----------+------------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Jack     | 20000</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Lucy     | 30000</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> Peter    | </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">(3 rows)</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="结语">结语<a href="https://ivorysql.org/zh-cn/blog/ivorysql-invisible-column#%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>不可见列功能是一项设计精妙的特性，为数据库开发和管理提供了更高的灵活性和安全性。通过合理利用不可见列，开发者可以轻松应对复杂的应用迁移场景，同时保持系统的稳定性和可扩展性。</p>
<p>如果您有正在使用 IvorySQL 数据库的项目，不妨尝试将此功能集成到您的解决方案中，提升整体效率和可靠性。</p>]]></content:encoded>
            <category>IvorySQL</category>
            <category>Database</category>
            <category>Oracle Compatible</category>
            <category>PostgreSQL</category>
            <category>Invisible Column</category>
            <category>不可见列</category>
        </item>
        <item>
            <title><![CDATA[IvorySQL 4.0 之兼容 Oracle 包功能设计思路解读]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/ivorysql-oracle-package-compatibility</link>
            <guid>https://ivorySQL.org/zh-cn/blog/ivorysql-oracle-package-compatibility</guid>
            <pubDate>Thu, 09 Jan 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[日前，IvorySQL 4.0 发布，该版本新增了兼容 Oracle 包功能的新特性。]]></description>
            <content:encoded><![CDATA[<p>日前，IvorySQL 4.0 发布，该版本新增了兼容 Oracle 包功能的新特性。</p>
<p>为了大家能够更好地理解和使用 IvorySQL 4.0，本文将简要介绍实现此功能时的设计思路。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="oracle-的包是什么">Oracle 的包是什么？<a href="https://ivorysql.org/zh-cn/blog/ivorysql-oracle-package-compatibility#oracle-%E7%9A%84%E5%8C%85%E6%98%AF%E4%BB%80%E4%B9%88" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>包是包含了逻辑上相关的 PL/SQL 类型、变量、常量、子过程、游标和异常的一个模式对象。包被编译并存储在数据库中，多个应用可以共享包的内容。</p>
<p>包总是有一个包规范，包规范中声明了公有对象，这些公有对象可以在包外被引用。</p>
<p>如果公有对象中包含了游标或子过程，则包必须有一个包体。包体必须定义公有游标和公有子过程的代码。包体也可以声明并定义私有对象，私有对象不能在包外被引用，但可用于包内使用。最后，包体可以有一个初始化部分，这部分用于初始化变量，做一些一次性的设置步骤和异常处理。修改包体的时候，可以不修改包规范或引用包的公有对象的数据库对象，因此可以认为包体是一个黑盒。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ivorysql-中包的实现">IvorySQL 中包的实现<a href="https://ivorysql.org/zh-cn/blog/ivorysql-oracle-package-compatibility#ivorysql-%E4%B8%AD%E5%8C%85%E7%9A%84%E5%AE%9E%E7%8E%B0" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>从内容来看，包体与嵌套子过程类似，包规范只是定义包体对外的接口，因此，从实现角度来看，包的实现过程可以和嵌套子过程类似。</p>
<p>我们主要处理的工作有如下几个方面：包的创建、更新、实例化、删除以及外部过程对包规范中包对象的引用。</p>
<ul>
<li class=""><strong>包的创建：</strong> 修改 psql 语法，使 psql 能将整个包的创建语句整体发到服务器，在服务器中增加包的创建语法，语法结构基本上和普通函数类似，因此与函数类似，无需在 SQL 端展开。包创建经过语法解析后走 DDL 流程，调用包的创建函数，将包的内容存储到系统表里面去。</li>
<li class=""><strong>包的更新：</strong> 在 SQL 端支持更新语法，经语法解析后，调用包的更新函数，更新系统表内容，调用 <code>plisql_package_Handler</code> 走一遍 <code>pl_gram.y</code>，并失效包规范元组或包体元组，这样避免在运行时编译包和包体。</li>
<li class=""><strong>包的删除：</strong> 需要在 SQL 端支持其删除语法，经语法解析后，调用包的删除函数，删除系统表该包的内容。</li>
<li class=""><strong>包的实例化：</strong> 在第一次引用包的时候，如果包的内容没有在内存中(具体来说是一个 hash 表，类似于 portal 的 hash 表存储)，则调用包的实例化函数，将包重新实例化，实例化其实是调用 PL/iSQL 端的 compile 函数，将包规范与包体重新编译，并将编译的结果放在当前进程的内存里，包的实例化应该是将包与包体的整体内容加载到内存中。</li>
<li class=""><strong>包对象引用：</strong> 在 parse 阶段，提供查找包规范中的变量，类型、子过程的接口，优先在本模式下查找包中类型，然后查找系统表中的类型，在查找子过程时，优先在嵌套函数、包中、系统表中查找。</li>
<li class=""><strong>包的失效与包的状态：</strong> 包中如果全是常量与类型，则包无状态，否则包是有状态的。包的状态在访问包的变量与函数时设置，在重建包时，会让包的本地实例失效，并且本地重新编译实例化，其他进程的包实例，如果包是有状态的，访问包中变量或类型，则首次访问报包的状态丢失错误，其后正常访问。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ivorysql-中包的设计">IvorySQL 中包的设计<a href="https://ivorysql.org/zh-cn/blog/ivorysql-oracle-package-compatibility#ivorysql-%E4%B8%AD%E5%8C%85%E7%9A%84%E8%AE%BE%E8%AE%A1" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="新增的系统表与缓存">新增的系统表与缓存<a href="https://ivorysql.org/zh-cn/blog/ivorysql-oracle-package-compatibility#%E6%96%B0%E5%A2%9E%E7%9A%84%E7%B3%BB%E7%BB%9F%E8%A1%A8%E4%B8%8E%E7%BC%93%E5%AD%98" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>为了存储包体与包规范内容，新增了 2 个系统表：</p>
<table><thead><tr><th>系统表名称</th><th>作用</th></tr></thead><tbody><tr><td><code>pg_package.h</code></td><td>存储包规范内容</td></tr><tr><td><code>pg_package_body.h</code></td><td>存储包体内容</td></tr></tbody></table>
<p>对应的系统缓存则有 4 个：</p>
<table><thead><tr><th>系统缓存名称</th><th>作用</th></tr></thead><tbody><tr><td><code>PKGBODYOID</code></td><td>根据包体的 OID 查找包体的元组</td></tr><tr><td><code>PKGBODYPKGID</code></td><td>根据包规范的 OID 查找包体的元组</td></tr><tr><td><code>PKGNAMEARGSNSP</code></td><td>根据包名和模式的 OID 查找包的元组</td></tr><tr><td><code>PKGOID</code></td><td>根据包规范的 OID 查找包的元组</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="包的实例化">包的实例化<a href="https://ivorysql.org/zh-cn/blog/ivorysql-oracle-package-compatibility#%E5%8C%85%E7%9A%84%E5%AE%9E%E4%BE%8B%E5%8C%96" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>包的实例化，类似于函数编译，是将用字符串定义的数据，转换成结构化数据。包的内容是由包规范和包体两部分构成，因此，包的编译需要进行一些特殊处理。增加相应的新函数分别编译包规范和包体，并将结果缓存到哈希表中。</p>
<p>另外，为了处理在删除包与包体的时候，本地缓存会失效，在创建包缓存的时候，注册一个包的失效函数，用来处理包的失效时，需要清除包的缓存状态。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">/* register invalid cache */</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">CacheRegisterSyscacheCallback(PKGBODYOID, InvalidatePackageCacheCallback, (Datum) 0);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">CacheRegisterSyscacheCallback(PKGOID, InvalidatePackageCacheCallback, (Datum) 0);</span><br></span></code></pre></div></div>
<p><code>InvalidatePackageCacheCallback</code> 将根据 hash 值，遍历 hash 表中的每一项，更新相应包的缓存状态。</p>
<p>包的缓存状态用一个 char 来表示，目前只用到三位 bit，<code>0x01</code> 表示包规范被更新了，<code>0x02</code> 表示包是否有包体，<code>0x04</code> 表示包体被更新了。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="包中对象的引用">包中对象的引用<a href="https://ivorysql.org/zh-cn/blog/ivorysql-oracle-package-compatibility#%E5%8C%85%E4%B8%AD%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%BC%95%E7%94%A8" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>提供查找包中函数、类型、变量的接口，供 parse 阶段使用，以下是部分函数列表。</p>
<table><thead><tr><th>函数名称</th><th>参数</th><th>返回值</th><th>说明</th></tr></thead><tbody><tr><td><code>LookupPkgTypeByTypename</code></td><td><code>Const List* names</code>，<code>Bool missing_ok</code></td><td><code>PkgType*</code></td><td>根据语法阶段构造类型名称列表，查看是否是包中的类型。</td></tr><tr><td><code>LookupPkgVarByvarnames</code></td><td><code>Const List _names</code>, <code>Bool missing_ok</code></td><td><code>PkgVar*</code></td><td>根据变量名称，查看是否是包中的变量</td></tr><tr><td><code>LookupPkgEntryByTypename</code></td><td><code>const List *names</code>, <code>bool missing_ok</code></td><td><code>PkgEntry </code></td><td>根据名称，返回包中属性（类型或变量）</td></tr><tr><td><code>LookupPkgFunc</code></td><td><code>ParseState *pstate</code>, <code>List *fargs, FuncCall *fn</code></td><td><code>FuncExpr</code></td><td>根据函数名称，查看是否是包中的函数</td></tr></tbody></table>
<p>在 PL/iSQL 非包内函数使用包的类型时，只需将类型的地址引用过来，而使用变量时，则需做一份本地映射，当涉及到该类的变量赋值时，需要进行特殊处理。一般来说，主要是切换到包的内存上下文，然后调用包的赋值函数。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="函数形参或返回值引用包的类型">函数形参或返回值引用包的类型<a href="https://ivorysql.org/zh-cn/blog/ivorysql-oracle-package-compatibility#%E5%87%BD%E6%95%B0%E5%BD%A2%E5%8F%82%E6%88%96%E8%BF%94%E5%9B%9E%E5%80%BC%E5%BC%95%E7%94%A8%E5%8C%85%E7%9A%84%E7%B1%BB%E5%9E%8B" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>该部分需要修改 <code>pg_proc</code> 系统表的结构，需要增加字段来记录参数类型和返回值类型的名称，故在系统表中增加参数类型名称和返回值类型名称两列即可解决。</p>
<p>类似于 <code>proargnames</code>，增加 <code>protypenames</code> 用于记录参数类型的类型名称，增加 <code>rettypename</code> 记录返回值类型的名称。只有当相关项引用包的类型时赋值，不然为空。</p>
<p>因为 <code>protypnames</code> 是一个 text 数组，因此当有一个函数参数是包中类型时，该数组就不为空，且参数类型为包的项是由 <code>TypeName</code> 结构字符序列化得到，其他非包的参数类型为空字符串。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="standard-包">standard 包<a href="https://ivorysql.org/zh-cn/blog/ivorysql-oracle-package-compatibility#standard-%E5%8C%85" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>支持 sys 模式下的 standard 包，可以不用带包名进行访问包中规范的对象，用户可以自行创建 standard 包。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="discard-package-语法">DISCARD PACKAGE 语法<a href="https://ivorysql.org/zh-cn/blog/ivorysql-oracle-package-compatibility#discard-package-%E8%AF%AD%E6%B3%95" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>该功能是为兼容 PostgreSQL 的 <code>DISCARD</code> 功能做的，目前 PostgreSQL 支持 <code>DISCARD SEQUENCE</code>、<code>DISCARD TEMP</code>、<code>DISCARD PLAN</code> 等用来删除当前 session 的序列、临时表以及计划等缓存，并且 DISCARD ALL 支持删除本 session 的 PORTAL、临时表、计划、序列等临时存储。</p>
<p>IvorySQL 支持 DISCARD PACKAGE 语法，并且在 DISCARD ALL 中调用函数删除本 session 的包缓存。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="逻辑备份还原支持包">逻辑备份还原支持包<a href="https://ivorysql.org/zh-cn/blog/ivorysql-oracle-package-compatibility#%E9%80%BB%E8%BE%91%E5%A4%87%E4%BB%BD%E8%BF%98%E5%8E%9F%E6%94%AF%E6%8C%81%E5%8C%85" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h3>
<p>在 <code>pg_dump</code> 工具中，包功能也得到了支持，用户可以使用 <code>pg_dump</code> 对包功能在内的数据进行备份恢复。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="总结">总结<a href="https://ivorysql.org/zh-cn/blog/ivorysql-oracle-package-compatibility#%E6%80%BB%E7%BB%93" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>以上就是实现兼容 Oracle 包功能时的设计思路。</p>
<p>通过包的形式将相关的功能模块化，使得数据库的过程、函数、变量和其他编程元素组织在一起形成自包含单元，便于管理和维护。由于实现细节隐藏在包体中，提高了代码的安全性和可维护性。包体中的代码在第一次调用时被加载到内存中，后续调用可以直接使用，减少了解析和加载时间。</p>]]></content:encoded>
            <category>IvorySQL</category>
            <category>Database</category>
            <category>Oracle Compatible</category>
            <category>PostgreSQL</category>
            <category>Oracle Package</category>
            <category>Package</category>
            <category>包功能</category>
        </item>
        <item>
            <title><![CDATA[IvorySQL 升级指南：从 3.x 到 4.0 的平滑过渡]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/ivorysql-upgrade-3.x-to-4.0</link>
            <guid>https://ivorySQL.org/zh-cn/blog/ivorysql-upgrade-3.x-to-4.0</guid>
            <pubDate>Thu, 02 Jan 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[日前，IvorySQL 4.0 重磅发布，全面支持 PostgreSQL 17，并且增强了对 Oracle 的兼容性。]]></description>
            <content:encoded><![CDATA[<p>日前，IvorySQL 4.0 重磅发布，全面支持 PostgreSQL 17，并且增强了对 Oracle 的兼容性。</p>
<p>本篇文章将详细描述，如何从 IvorySQL 3.x 升级到 IvorySQL 4.0。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="pg_upgrade-工具介绍"><code>pg_upgrade</code> 工具介绍<a href="https://ivorysql.org/zh-cn/blog/ivorysql-upgrade-3.x-to-4.0#pg_upgrade-%E5%B7%A5%E5%85%B7%E4%BB%8B%E7%BB%8D" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p><code>pg_upgrade</code> 工具是 PostgreSQL 内置的跨版本升级工具，能够对数据库就地升级，不需要执行导出和导入操作。IvorySQL 源自于 PG，因此也能够使用 <code>pg_upgrade</code> 工具进行大版本升级。</p>
<p><code>pg_upgrade</code> 提供了升级前的兼容性检查（<code>-c</code> 或者 <code>--check</code> 选项）功能，可以发现插件、数据类型不兼容等问题。如果指定了 <code>--link</code> 选项，新版本服务可以直接使用原有的数据库文件而不需要执行复制，通常可以在几分钟内完成升级操作。</p>
<p>常用的参数包括：</p>
<ul>
<li class=""><code>-b bindir，--old-bindir=bindir</code>：旧的 IvorySQL&nbsp; 可执行文件目录</li>
<li class=""><code>-B bindir，--new-bindir=bindir</code>：新的 IvorySQL 可执行文件目录</li>
<li class=""><code>-d configdir，--old-datadir=configdir</code>：旧版本的数据目录</li>
<li class=""><code>-D configdir，--new-datadir=configdir</code>：新版本的数据目录</li>
<li class=""><code>-c，--check</code>：只检查升级兼容性，不更改任何数据</li>
<li class=""><code>-k，--link</code>：硬链接方式升级</li>
</ul>
<p>下面介绍一下在 CentOS 8 平台上如何使用 <code>pg_upgrade</code> 将 IvorySQL 升级到最新的 4.0 版本。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="升级准备">升级准备<a href="https://ivorysql.org/zh-cn/blog/ivorysql-upgrade-3.x-to-4.0#%E5%8D%87%E7%BA%A7%E5%87%86%E5%A4%87" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>首先停止旧版本的 IvorySQL 3.4 数据库：</p>
<p><img decoding="async" loading="lazy" alt="img" src="data:image/png;base64,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" width="834" height="64" class="img_ev3q"></p>
<p>然后安装新版本的 IvorySQL 4.0 数据库：</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/up-to-4.0-2-4563181ec2bad457c0a3d4128446a4ed.png" width="1160" height="466" class="img_ev3q"></p>
<p>初始化新版 IvorySQL 4.0 数据目录：</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/up-to-4.0-3-4ff2c0afcb6fb47d079ab171e261f9e5.png" width="687" height="183" class="img_ev3q"></p>
<p>检查版本兼容性：</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/up-to-4.0-4-00c330bd4aead0fffb85712501b2a4b7.png" width="1171" height="322" class="img_ev3q"></p>
<p>最后出现 <code>Clusters are compatible</code> 表明两个版本之间的数据不存在兼容性问题，可以进行升级。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="正式升级">正式升级<a href="https://ivorysql.org/zh-cn/blog/ivorysql-upgrade-3.x-to-4.0#%E6%AD%A3%E5%BC%8F%E5%8D%87%E7%BA%A7" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/up-to-4.0-5-999ed695e72bae1ac12ce5ac9535d8dc.png" width="1159" height="410" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/up-to-4.0-6-5682919a2e7af771ba72f93045ce233a.png" width="1140" height="665" class="img_ev3q"></p>
<p>看到 <code>Upgrade&nbsp;Complete</code> 说明升级已经顺利完成。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="更新统计信息">更新统计信息<a href="https://ivorysql.org/zh-cn/blog/ivorysql-upgrade-3.x-to-4.0#%E6%9B%B4%E6%96%B0%E7%BB%9F%E8%AE%A1%E4%BF%A1%E6%81%AF" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p><code>pg_upgrade</code> 会创建新的系统表，并重用旧的数据进行升级，统计信息并不会随升级过程迁移，所以在启用新版本之前，应该首先重新收集统计信息，避免没有统计信息导致错误的查询计划。</p>
<p>首先启动新版本数据库：</p>
<p><img decoding="async" loading="lazy" alt="img" src="data:image/png;base64,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" width="849" height="61" class="img_ev3q"></p>
<p>手动运行 <code>vacuum</code> 命令：</p>
<p><img decoding="async" loading="lazy" alt="img" src="https://ivorysql.org/zh-cn/assets/images/up-to-4.0-8-be2c7b52deda5bab3fd79616a2f8986a.png" width="880" height="206" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="升级后的清理">升级后的清理<a href="https://ivorysql.org/zh-cn/blog/ivorysql-upgrade-3.x-to-4.0#%E5%8D%87%E7%BA%A7%E5%90%8E%E7%9A%84%E6%B8%85%E7%90%86" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>确认没有问题后删除旧库：</p>
<p><img decoding="async" loading="lazy" alt="img" src="data:image/png;base64,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" width="547" height="103" class="img_ev3q"></p>
<p>至此，我们就完成了整个升级过程。</p>
<p>如果您在后续的升级过程中，遇到问题，可以在 GitHub 上提交 <a href="https://github.com/IvorySQL/IvorySQL/issues" target="_blank" rel="noopener noreferrer" class="">issue</a>，我们将及时为您解答。同时，如果您对 IvorySQL 后续的功能迭代有任何想法或者期待，也可以提交 issue 反馈给我们。</p>]]></content:encoded>
            <category>IvorySQL</category>
            <category>Database</category>
            <category>Oracle Compatible</category>
            <category>PostgreSQL</category>
        </item>
        <item>
            <title><![CDATA[IvorySQL 数据库中的外部存储管理器]]></title>
            <link>https://ivorySQL.org/zh-cn/blog/ivorysql-external-storage-manager</link>
            <guid>https://ivorySQL.org/zh-cn/blog/ivorysql-external-storage-manager</guid>
            <pubDate>Thu, 05 Dec 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[一般来说，数据库的存储系统分为内存和外存两个部分，而除了内存数据库之外，数据最终都是持久化存储的，持久化就要将数据从内存 buffer 落盘到外存，因此今天我们来聊聊 IvorySQL 中的外部存储管理器。]]></description>
            <content:encoded><![CDATA[<p>一般来说，数据库的存储系统分为内存和外存两个部分，而除了内存数据库之外，数据最终都是持久化存储的，持久化就要将数据从内存 buffer 落盘到外存，因此今天我们来聊聊 IvorySQL 中的外部存储管理器。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="存储文件的分类">存储文件的分类<a href="https://ivorysql.org/zh-cn/blog/ivorysql-external-storage-manager#%E5%AD%98%E5%82%A8%E6%96%87%E4%BB%B6%E7%9A%84%E5%88%86%E7%B1%BB" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>IvorySQL 数据库使用的存储管理器基本上与 PostgreSQL 相同，其文件目录及文件组织结构如下图：</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://ivorysql.org/zh-cn/assets/images/20241205-1-81c420b46fe7becf37e20f7b7ad62dfc.png" width="800" height="326" class="img_ev3q"></p>
<p>从存储的角度看，文件管理分为 3 个主要部分：</p>
<ul>
<li class="">配置相关的文件，比如 <code>pg_control</code>，<code>postgresql.conf</code>，<code>pg_hba.conf</code>，<code>PG_VERSION</code> 等；</li>
<li class="">WAL 日志文件，<code>000000010000000000000001</code>， <code>000000010000000000000002</code> 等；</li>
<li class="">表，索引的数据记录文件，相应的剩余空间和可见性映射文件等。</li>
</ul>
<p>配置相关的文件在 PostgreSQL 中使用标准文件读写函数处理，比如：记录数据库状态和操作的 <code>pg_control</code> 文件。其中后台模块通过 <code>ReadControlFile()</code>，<code>WriteControlFile()</code>，<code>UpdateControlFile()</code>&nbsp; 进行读写操作。前台工具通过 <code>get_controlfile()</code>，<code>update_controlfile()</code>&nbsp; 进行读写操作。无论前台还是后台，这些读写操作最终还是通过标准文件操作函数 <code>open()</code>，<code>read()</code>，<code>write()</code>&nbsp; 进行。这些对配置文件的直接操作不在 SMGR 管控范畴。</p>
<p>WAL 日志文件最终也是通过标准文件函数 <code>read()</code>，<code>write()</code> 等等完成读写（和配置文件类似）。但不同的是，文件名需要根据 LSN 动态计算获取。</p>
<p>表，索引数据记录存储在 base 目录下的相应数据库的子目录下。集群级别的 catalog（系统表）数据记录变化则反映在 global 目录下。系统表的管理与用户表类似，同样采用 OID 的命名方式。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="sql-语句访问存储介质过程-">SQL 语句访问存储介质过程 &nbsp;<a href="https://ivorysql.org/zh-cn/blog/ivorysql-external-storage-manager#sql-%E8%AF%AD%E5%8F%A5%E8%AE%BF%E9%97%AE%E5%AD%98%E5%82%A8%E4%BB%8B%E8%B4%A8%E8%BF%87%E7%A8%8B-" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>一条 SQL 语句通常按照如下路径访问一个磁盘上的数据记录：</p>
<ul>
<li class="">首先语句先经过 5 阶段的查询处理：parse，rewrite，analyze，plan，execute。</li>
<li class="">再进到 Table/Index Access Method 层。</li>
<li class="">Access Method 层通常会使用 Buffer Manager 服务，对数据条目进行操作，根据操作决定是否将数据块标记为“脏”。</li>
<li class="">Buffer Manager 层会使用 Storage Manager 服务，根据 buffer tag 调用 <code>smgr_read()</code> 或 <code>smgr_write()</code> 来读取或写数据到存储介质。</li>
</ul>
<p><img decoding="async" loading="lazy" alt="image" src="https://ivorysql.org/zh-cn/assets/images/20241205-2-45a8c4c03cd3924a611d28e5f9ef8e88.png" width="883" height="332" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="smgr-api-接口-">SMGR API 接口 &nbsp;<a href="https://ivorysql.org/zh-cn/blog/ivorysql-external-storage-manager#smgr-api-%E6%8E%A5%EF%BF%BD%EF%BF%BD%E5%8F%A3-" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<p>PostgreSQL 原本有许多 SMGR API 接口的实现，但目前只留下 Magnetic Disk（md.c）的实现（磁盘)。实际上，磁盘管理器可以支持任何类型的设备，只要操作系统为该设备提供了标准的文件系统操作接口。但是保留存储管理器（smgr.c）这个中间层，以防有人引入其他类型的存储管理器。删除中间层不会节省明显的开销，因为对存储介质的操作，比一层 c 语言函数调用昂贵的多的多。<code>f_smgr</code> 是一组函数指针结构，包含了 PostgreSQL 对存储所需要的函数。<code>smgr.c</code> 定义所有函数接口的 wrapper 函数，这些 wrapper 会最终调用实际注册的实现函数，也就是 <code>mdXXXX</code> 函数。</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">static</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> f_smgr smgrsw</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/* magnetic disk */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_init </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mdinit</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_shutdown </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_open </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mdopen</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_close </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mdclose</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_create </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mdcreate</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_exists </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mdexists</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_unlink </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mdunlink</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_extend </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mdextend</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_prefetch </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mdprefetch</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_read </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mdread</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_write </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mdwrite</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_writeback </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mdwriteback</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_nblocks </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mdnblocks</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_truncate </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mdtruncate</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">smgr_immedsync </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mdimmedsync</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>SMgrRelation 是一个重要的结构体，几乎所有和 SMGR 相关的函数都需要这个结构。</p>
<p><img decoding="async" loading="lazy" alt="image" src="https://ivorysql.org/zh-cn/assets/images/20241205-3-de0b8bf23c570f764841c5aa2e33160a.png" width="1197" height="923" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="smgr-关键接口函数-">SMGR 关键接口函数 &nbsp;<a href="https://ivorysql.org/zh-cn/blog/ivorysql-external-storage-manager#smgr-%E5%85%B3%E9%94%AE%E6%8E%A5%E5%8F%A3%E5%87%BD%E6%95%B0-" class="hash-link" aria-label="直接链接到标题" title="直接链接到标题" translate="no">​</a></h2>
<ul>
<li class="">
<p><code>Smgrread()</code> 根据 5 个参数：tablespace，database，relation，forknum 和 blocknum 定位一个 8K 数据块，并将其读入指定的内存中。</p>
<p><code>smgrread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char * buffer)</code></p>
</li>
<li class="">
<p><code>Smgrwrite()</code> 根据 5 个参数定位一个 8K 数据块，然后用指定内存中的值将其覆盖。根据 skipFsync 决定是否在返回前保证数据落盘成功。</p>
<p><code>smgrwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char * buffer, bool skipFsync)</code></p>
</li>
<li class="">
<p>smgrextend 负责在当前文件中扩展一个新的 8K 块并写入指定内存中的数据，其他与 smgrwrite 相同。</p>
<p><code>smgrextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char * buffer, bool skipFsync)</code></p>
</li>
<li class="">
<p>smgrnblocks 返回指定表当前的 8K 数据块总数，这个值对语句处理的 planner 环节很重要。</p>
<p><code>smgrnblocks(SMgrRelation reln, ForkNumber forknum)</code></p>
</li>
<li class="">
<p>对象共同存在。</p>
<p><code>smgropen(RelFileNode rnode, BackendId backend)</code></p>
</li>
<li class="">
<p>smgrclose 负责从哈希表中删除指定的 SMgrRelation 对象。</p>
<p><code>smgrclose(SMgrRelation reln)</code></p>
</li>
<li class="">
<p>smgrtruncate 用于从文件（数据，剩余空间，可见性）中从后往前去删除指定数量的 8K 数据块，可以一次对上述 3 个 fork 文件分别去除多个数据块，最终缩小文件尺寸。</p>
<p><code>smgrtruncate(SMgrRelation reln, ForkNumber *forknum, int nforks, BlockNumber *nblocks)</code></p>
</li>
<li class="">
<p>smgrprefetch 利用 POSIX_FADV_WILLNEED 提供的服务要求操作系统提前读取磁盘数据块到缓存中，这个动作可避免磁盘 I/O 瓶颈。</p>
<p><code>smgrprefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)</code></p>
</li>
</ul>
<p>smgrwrite 和 smgrextend 是写磁盘相关的重要函数，也是影响系统 I/O 性能的关键。它们在以下场景下被调用：</p>
<ul>
<li class="">smgrwrite：<!-- -->
<ul>
<li class="">当一个缓冲区（Buffer）被修改（即脏数据）时，调用 smgrwrite 将数据写到磁盘上的对应文件中；</li>
<li class="">当事务提交时，需要确保已经将所有的脏数据写回磁盘，这就需要调用 smgrwrite；</li>
<li class="">在 VACUUM 操作中，当对表的可见性信息进行更新时，相应的缓冲区可能会变为脏数据，需要通过 smgrwrite 将其写进磁盘；</li>
</ul>
</li>
<li class="">smgrextend：<!-- -->
<ul>
<li class="">当需要扩展一个文件（表或索引）时，调用 smgrextend 将文件扩展到所需的大小；</li>
<li class="">创建一个新表或索引时，需要确定其初始大小，也会调用 smgrextend；</li>
<li class="">执行 CLUSTER 操作时，需要将表重新组织，也可能会调用 smgrextend；</li>
<li class="">smgrwrite 和 smgrextend 用于确保数据的持久性，smgrwrite 用于将修改的数据写进磁盘，而 smgrextend 用于扩展文件大小。</li>
</ul>
</li>
</ul>]]></content:encoded>
            <category>IvorySQL</category>
            <category>Database</category>
            <category>Storage Manager</category>
            <category>PostgreSQL</category>
        </item>
    </channel>
</rss>