<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Pg-Partman on Tarragon</title><link>https://tarrragon.github.io/blog/tags/pg-partman/</link><description>Recent content in Pg-Partman on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 22 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/pg-partman/index.xml" rel="self" type="application/rss+xml"/><item><title>PostgreSQL pg_partman Advanced</title><link>https://tarrragon.github.io/blog/backend/01-database/vendors/postgresql/pg-partman-advanced/</link><pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/01-database/vendors/postgresql/pg-partman-advanced/</guid><description>&lt;p>PostgreSQL pg_partman advanced 的核心責任是把 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/table-partitioning/" data-link-title="Table Partitioning" data-link-desc="說明單一資料庫內如何把大表拆成多個分區，並由查詢規劃器只掃相關片段">declarative partitioning&lt;/a> 的日常維護自動化。pg_partman 可以協助建立未來 partition、管理 retention、執行 maintenance job，讓 time-based 或 serial-based partition 不再依賴人工 DDL。&lt;/p>
&lt;p>本文的判讀錨點是：pg_partman 解決的是 partition lifecycle operation，而非 partition strategy 本身。Partition key、query pattern、retention、index、foreign key 與 migration 仍要先在 &lt;a href="../declarative-partitioning/">Declarative Partitioning&lt;/a> 與 &lt;a href="../partition-redesign/">Partition Redesign&lt;/a> 做對。&lt;/p>
&lt;h2 id="responsibility-boundary">Responsibility Boundary&lt;/h2>
&lt;p>Responsibility boundary 的核心責任是區分 PostgreSQL 原生 partition 和 pg_partman。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>層級&lt;/th>
 &lt;th>責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>PostgreSQL declarative partitioning&lt;/td>
 &lt;td>partition table、constraint、planner pruning&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>pg_partman&lt;/td>
 &lt;td>future partition premake、retention、maintenance&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Scheduler / job runner&lt;/td>
 &lt;td>定期執行 maintenance&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>DBA / platform&lt;/td>
 &lt;td>monitoring、backup、DDL review&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Application&lt;/td>
 &lt;td>query pattern、partition key 使用&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>pg_partman 的價值在於減少重複 DDL。它不會替 application 選出正確 partition key，也不會自動修復跨 partition query 設計。&lt;/p>
&lt;h2 id="core-concepts">Core Concepts&lt;/h2>
&lt;p>Core concepts 的核心責任是理解 pg_partman operation vocabulary。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>概念&lt;/th>
 &lt;th>意義&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Parent table&lt;/td>
 &lt;td>partitioned table 的入口&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Child table&lt;/td>
 &lt;td>實際存放資料的 partition&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Premake&lt;/td>
 &lt;td>預先建立未來 partition&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Retention&lt;/td>
 &lt;td>自動 detach / drop 舊 partition&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Maintenance&lt;/td>
 &lt;td>建立新 partition、處理 retention 的 job&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Template&lt;/td>
 &lt;td>child partition 繼承 index / constraint 的模板&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Premake 是防止 insert 打到不存在 partition 的保護。若 partition 建立落後於時間，application insert 會失敗或落到 default partition；production 要對 future partition count 設 alert。&lt;/p>
&lt;p>Retention 是資料生命週期操作。Drop 舊 partition 速度快，但要先確認 legal retention、backup、analytics dependency 與 downstream CDC。&lt;/p>
&lt;h2 id="setup-pattern">Setup Pattern&lt;/h2>
&lt;p>Setup pattern 的核心責任是把 pg_partman 導入流程放進 migration gate。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">EXTENSION&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IF&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXISTS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pg_partman&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">events&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">bigserial&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">tenant_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">uuid&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">created_at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">timestamptz&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">payload&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">jsonb&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PARTITION&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RANGE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">created_at&lt;/span>&lt;span class="p">);&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>實際建立 partman config 要依 pg_partman 版本與 provider 支援文件執行。Managed PostgreSQL 可能限制 extension version、background worker 或 scheduler，因此 setup 前要先確認 provider boundary。&lt;/p></description><content:encoded><![CDATA[<p>PostgreSQL pg_partman advanced 的核心責任是把 <a href="/blog/backend/knowledge-cards/table-partitioning/" data-link-title="Table Partitioning" data-link-desc="說明單一資料庫內如何把大表拆成多個分區，並由查詢規劃器只掃相關片段">declarative partitioning</a> 的日常維護自動化。pg_partman 可以協助建立未來 partition、管理 retention、執行 maintenance job，讓 time-based 或 serial-based partition 不再依賴人工 DDL。</p>
<p>本文的判讀錨點是：pg_partman 解決的是 partition lifecycle operation，而非 partition strategy 本身。Partition key、query pattern、retention、index、foreign key 與 migration 仍要先在 <a href="../declarative-partitioning/">Declarative Partitioning</a> 與 <a href="../partition-redesign/">Partition Redesign</a> 做對。</p>
<h2 id="responsibility-boundary">Responsibility Boundary</h2>
<p>Responsibility boundary 的核心責任是區分 PostgreSQL 原生 partition 和 pg_partman。</p>
<table>
  <thead>
      <tr>
          <th>層級</th>
          <th>責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>PostgreSQL declarative partitioning</td>
          <td>partition table、constraint、planner pruning</td>
      </tr>
      <tr>
          <td>pg_partman</td>
          <td>future partition premake、retention、maintenance</td>
      </tr>
      <tr>
          <td>Scheduler / job runner</td>
          <td>定期執行 maintenance</td>
      </tr>
      <tr>
          <td>DBA / platform</td>
          <td>monitoring、backup、DDL review</td>
      </tr>
      <tr>
          <td>Application</td>
          <td>query pattern、partition key 使用</td>
      </tr>
  </tbody>
</table>
<p>pg_partman 的價值在於減少重複 DDL。它不會替 application 選出正確 partition key，也不會自動修復跨 partition query 設計。</p>
<h2 id="core-concepts">Core Concepts</h2>
<p>Core concepts 的核心責任是理解 pg_partman operation vocabulary。</p>
<table>
  <thead>
      <tr>
          <th>概念</th>
          <th>意義</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Parent table</td>
          <td>partitioned table 的入口</td>
      </tr>
      <tr>
          <td>Child table</td>
          <td>實際存放資料的 partition</td>
      </tr>
      <tr>
          <td>Premake</td>
          <td>預先建立未來 partition</td>
      </tr>
      <tr>
          <td>Retention</td>
          <td>自動 detach / drop 舊 partition</td>
      </tr>
      <tr>
          <td>Maintenance</td>
          <td>建立新 partition、處理 retention 的 job</td>
      </tr>
      <tr>
          <td>Template</td>
          <td>child partition 繼承 index / constraint 的模板</td>
      </tr>
  </tbody>
</table>
<p>Premake 是防止 insert 打到不存在 partition 的保護。若 partition 建立落後於時間，application insert 會失敗或落到 default partition；production 要對 future partition count 設 alert。</p>
<p>Retention 是資料生命週期操作。Drop 舊 partition 速度快，但要先確認 legal retention、backup、analytics dependency 與 downstream CDC。</p>
<h2 id="setup-pattern">Setup Pattern</h2>
<p>Setup pattern 的核心責任是把 pg_partman 導入流程放進 migration gate。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="n">EXTENSION</span><span class="w"> </span><span class="k">IF</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">EXISTS</span><span class="w"> </span><span class="n">pg_partman</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"></span><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">events</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">  </span><span class="n">id</span><span class="w"> </span><span class="n">bigserial</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">  </span><span class="n">tenant_id</span><span class="w"> </span><span class="n">uuid</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">  </span><span class="n">created_at</span><span class="w"> </span><span class="n">timestamptz</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">  </span><span class="n">payload</span><span class="w"> </span><span class="n">jsonb</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w">
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="w"></span><span class="p">)</span><span class="w"> </span><span class="n">PARTITION</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">RANGE</span><span class="w"> </span><span class="p">(</span><span class="n">created_at</span><span class="p">);</span></span></span></code></pre></div><p>實際建立 partman config 要依 pg_partman 版本與 provider 支援文件執行。Managed PostgreSQL 可能限制 extension version、background worker 或 scheduler，因此 setup 前要先確認 provider boundary。</p>
<p>最小 setup evidence：</p>
<ol>
<li>Extension version。</li>
<li>Parent table DDL。</li>
<li>Partition key 與 interval。</li>
<li>Premake 數量。</li>
<li>Retention policy。</li>
<li>Maintenance job schedule。</li>
<li>Test insert 到 current / future partition。</li>
</ol>
<h2 id="maintenance-runbook">Maintenance Runbook</h2>
<p>Maintenance runbook 的核心責任是讓 partition lifecycle 可觀測。</p>
<table>
  <thead>
      <tr>
          <th>Signal</th>
          <th>意義</th>
          <th>反應</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>future partition count</td>
          <td>premake 是否足夠</td>
          <td>手動跑 maintenance、修 scheduler</td>
      </tr>
      <tr>
          <td>default partition rows</td>
          <td>routing 失敗或 partition 缺漏</td>
          <td>建 partition、搬資料、修 app timestamp</td>
      </tr>
      <tr>
          <td>old partition count</td>
          <td>retention 是否執行</td>
          <td>檢查 policy、legal hold、job error</td>
      </tr>
      <tr>
          <td>maintenance duration</td>
          <td>DDL / lock / catalog 壓力</td>
          <td>調整 schedule、拆 table</td>
      </tr>
      <tr>
          <td>index build time</td>
          <td>child index 建立成本</td>
          <td>template / concurrent strategy review</td>
      </tr>
  </tbody>
</table>
<p>Maintenance job 要有 owner。Cron、pg_cron、background worker、Kubernetes job 或 managed scheduler 都可以；重點是 job failure 會告警，並且有人處理。</p>
<h2 id="migration-and-backfill">Migration and Backfill</h2>
<p>Migration and backfill 的核心責任是把既有大表轉成 partman-managed partition。這通常比新表導入更高風險。</p>
<table>
  <thead>
      <tr>
          <th>Phase</th>
          <th>Evidence</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Audit</td>
          <td>table size、query pattern、write rate</td>
      </tr>
      <tr>
          <td>New schema</td>
          <td>parent table、child partition、index</td>
      </tr>
      <tr>
          <td>Backfill</td>
          <td>batch size、lag、lock、checksum</td>
      </tr>
      <tr>
          <td>Dual write</td>
          <td>app compatibility</td>
      </tr>
      <tr>
          <td>Cutover</td>
          <td>rename / view / routing switch</td>
      </tr>
      <tr>
          <td>Cleanup</td>
          <td>old table retention、rollback</td>
      </tr>
  </tbody>
</table>
<p>Backfill 要控制 WAL、replica lag、autovacuum、index bloat 與 lock。大型 table 應先用 shadow table 或 partition redesign playbook，避開 peak traffic 直接重建。</p>
<h2 id="failure-modes">Failure Modes</h2>
<p>Failure modes 的核心責任是列出 pg_partman 常見事故。</p>
<table>
  <thead>
      <tr>
          <th>Failure mode</th>
          <th>判讀訊號</th>
          <th>修正方向</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>未建立未來 partition</td>
          <td>insert 失敗或 default partition 增長</td>
          <td>補 partition、修 maintenance schedule</td>
      </tr>
      <tr>
          <td>retention drop 過早</td>
          <td>查詢缺歷史資料</td>
          <td>restore backup、調 policy、legal review</td>
      </tr>
      <tr>
          <td>managed provider 不支援</td>
          <td>extension / worker 限制</td>
          <td>改 manual partition job 或 provider</td>
      </tr>
      <tr>
          <td>index / constraint 漂移</td>
          <td>child partition schema 不一致</td>
          <td>template review、schema diff</td>
      </tr>
      <tr>
          <td>planner pruning 失效</td>
          <td>query 未帶 partition key</td>
          <td>query rewrite、index review</td>
      </tr>
  </tbody>
</table>
<p>pg_partman 事故通常是 lifecycle 事故。Runbook 要先看 maintenance job，再看 partition metadata 與 application query。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>pg_partman advanced 完成後，partition 設計讀 <a href="../declarative-partitioning/">Declarative Partitioning</a>；重排策略讀 <a href="../partition-redesign/">Partition Redesign</a>；migration gate 讀 <a href="../online-schema-change/">Online Schema Change</a>。</p>
]]></content:encoded></item></channel></rss>