<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Database-Performance on K-Life Hack | システムアーキテクチャ &amp; DevOps</title><link>https://klifehack.com/tags/database-performance/</link><description>Recent content in Database-Performance on K-Life Hack | システムアーキテクチャ &amp; DevOps</description><generator>Hugo -- gohugo.io</generator><language>ja</language><lastBuildDate>Fri, 11 Sep 2026 10:30:15 +0900</lastBuildDate><atom:link href="https://klifehack.com/tags/database-performance/index.xml" rel="self" type="application/rss+xml"/><item><title>pg_prewarmによるPostgreSQL Shared Buffersの事前ロードとAuroraスケーリング制御</title><link>https://klifehack.com/p/postgresql-pg-prewarm-cache-strategy/</link><pubDate>Fri, 11 Sep 2026 10:30:15 +0900</pubDate><guid>https://klifehack.com/p/postgresql-pg-prewarm-cache-strategy/</guid><description>&lt;p&gt;インフラストラクチャの運用において、データベースノードの再起動やメンテナンス直後は、メモリ上のShared Buffersが初期化された状態（コールドスタート）となります。この状態で高トラフィックが流入した場合、PostgreSQLはデータブロックをストレージから同期的に読み込む必要が生じ、ディスクI/Oのボトルネックおよびクエリレイテンシの大幅な悪化を引き起こします。&lt;/p&gt;&#10;&lt;p&gt;このようなウォームアップ遅延を未然に防止し、予測可能なI/Oパフォーマンスを確保するため、標準モジュールである&lt;code&gt;pg_prewarm&lt;/code&gt;を用いたキャッシュの事前ロード技術と、PostgreSQL 11以降で利用可能な&lt;code&gt;autoprewarm&lt;/code&gt;バックグラウンドワーカーの動作メカニズムを整理します。&lt;/p&gt;&#10;&lt;h2 id="pg_prewarmの基本構造と動作モード"&gt;pg_prewarmの基本構造と動作モード&#10;&lt;/h2&gt;&lt;p&gt;&lt;code&gt;pg_prewarm&lt;/code&gt;は、指定したリレーション（テーブルまたはインデックス）のデータブロックを、OSのページキャッシュまたはPostgreSQLのShared Buffersに直接読み込む機能を提供します。&lt;/p&gt;&#10;&lt;p&gt;基本設定手順として、データベース内で拡張機能を有効化します。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; EXTENSION &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;EXISTS&lt;/span&gt; pg_prewarm;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;リレーションをShared Buffersへロードする基本クエリは以下の通りです。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; pg_prewarm(&lt;span style="color:#e6db74"&gt;&amp;#39;target_relation_name&amp;#39;&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;pg_prewarm&lt;/code&gt;関数の詳細なシグネチャと各パラメータの仕様は以下の通り設定可能です。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pg_prewarm(&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; relation regclass,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;mode&lt;/span&gt; text &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;buffer&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fork text &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;main&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; first_block bigint &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; last_block bigint &lt;span style="color:#66d9ef"&gt;DEFAULT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;) &lt;span style="color:#66d9ef"&gt;RETURNS&lt;/span&gt; bigint&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;各パラメータの機能定義：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;b&gt;&lt;code&gt;relation&lt;/code&gt; (&lt;code&gt;regclass&lt;/code&gt;)&lt;/b&gt;: 対象となるテーブル名、インデックス名、またはOIDを指定します。&lt;/li&gt;&#10;&lt;li&gt;&lt;b&gt;&lt;code&gt;mode&lt;/code&gt; (&lt;code&gt;text&lt;/code&gt;)&lt;/b&gt;: メモリ転送のメカニズムを選択します。&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;buffer&lt;/code&gt;: PostgreSQLのShared Buffersへ直接ロードを実行します（デフォルト）。&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;read&lt;/code&gt;: Shared Buffersを経由せず、OSのページキャッシュへ同期読み込み（&lt;code&gt;read()&lt;/code&gt;）を行います。&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;prefetch&lt;/code&gt;: OSの非同期プリフェッチ機能（&lt;code&gt;posix_fadvise()&lt;/code&gt;）を発行します（OS非対応時は同期読み込みへフォールバックします）。&lt;/li&gt;&#10;&lt;li&gt;&lt;b&gt;&lt;code&gt;fork&lt;/code&gt; (&lt;code&gt;text&lt;/code&gt;)&lt;/b&gt;: ロード対象のファイルフォークを指定します。通常データは&lt;code&gt;main&lt;/code&gt;、空き領域管理は&lt;code&gt;fsm&lt;/code&gt;、可視性マップは&lt;code&gt;vm&lt;/code&gt;を指定します。&lt;/li&gt;&#10;&lt;li&gt;&lt;b&gt;&lt;code&gt;first_block&lt;/code&gt; / &lt;code&gt;last_block&lt;/code&gt; (&lt;code&gt;bigint&lt;/code&gt;)&lt;/b&gt;: 読み込み対象の8KBブロック番号範囲を制限します。&lt;code&gt;NULL&lt;/code&gt;の場合、全ブロックが処理されます。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="autoprewarmによるバッファ状態の自動復元"&gt;autoprewarmによるバッファ状態の自動復元&#10;&lt;/h2&gt;&lt;p&gt;PostgreSQL 11以降では、&lt;code&gt;pg_prewarm&lt;/code&gt;拡張機能に自動復元サブシステム&lt;code&gt;autoprewarm&lt;/code&gt;が統合されています。サーバ停止直前のShared Buffersの状態を記録し、再起動時に自動的に該当ブロックを再ロードします。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;postgresql.conf&lt;/code&gt;における必須設定パラメータ：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;shared_preload_libraries&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;pg_prewarm&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;pg_prewarm.autoprewarm&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;pg_prewarm.autoprewarm_interval&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;300s&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="動作機構"&gt;動作機構&#10;&lt;/h3&gt;&lt;ol&gt;&#10;&lt;li&gt;&lt;b&gt;バックグラウンドダンプ (&lt;code&gt;autoprewarm master&lt;/code&gt;)&lt;/b&gt;:&#10;&lt;code&gt;autoprewarm master&lt;/code&gt;ワーカープロセスが定期的にShared Buffers上のマッピング情報を走査し、&lt;code&gt;$PGDATA/autoprewarm.blocks&lt;/code&gt;ファイルへブロックIDリストを永続化します（既定値: 300秒間隔）。&lt;/li&gt;&#10;&lt;li&gt;&lt;b&gt;起動時自動ロード&lt;/b&gt;:&#10;PostgreSQL起動時、専用のバックグラウンドワーカーが&lt;code&gt;autoprewarm.blocks&lt;/code&gt;を読み込み、ディスクからShared Buffersへ対象ブロックを順次再ロードします。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="aws-aurora-postgresqlにおけるレプリカウォームアップ自動化"&gt;AWS Aurora PostgreSQLにおけるレプリカウォームアップ自動化&#10;&lt;/h2&gt;&lt;p&gt;クラウドネイティブ環境であるAWS Aurora PostgreSQLでは、ストレージと計算ノードが分離されていますが、各DBインスタンスローカルのShared Buffers管理は依然として重要です。Auto Scalingにより増設されたRead Replicaは、起動直後は空のバッファ状態となります。&lt;/p&gt;&#10;&lt;p&gt;新規レプリカ起動時のレイテンシ増加を回避するため、Amazon EventBridgeとAWS Lambdaを連携させた自動ウォームアップ構成が活用されます。&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;[Auto Scaling Event]&#10; │&#10; ▼&#10;[Amazon EventBridge] (インスタンス作成イベントを検知)&#10; │&#10; ▼&#10;[AWS Lambda Function] (SQL実行タスクを起動)&#10; │&#10; ▼&#10;[Aurora Read Replica] ──&amp;amp;gt; pg_prewarm() 実行 ──&amp;amp;gt; メモリにキャッシュロード&#10;&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&#10;&lt;li&gt;Aurora Auto Scalingが新ターゲットをプロビジョニング。&lt;/li&gt;&#10;&lt;li&gt;EventBridgeが完了イベントをキャッチ。&lt;/li&gt;&#10;&lt;li&gt;AWS Lambdaが起動し、新レプリカノードへ接続。&lt;/li&gt;&#10;&lt;li&gt;事前に定義されたクリティカルテーブル群に対して&lt;code&gt;pg_prewarm&lt;/code&gt;を実行し、トラフィックルーティング前にキャッシュを完成させます。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="troubleshooting"&gt;Troubleshooting&#10;&lt;/h2&gt;&lt;h3 id="1-cache-thrashingキャッシュスラッシングの発生"&gt;1. Cache Thrashing（キャッシュスラッシング）の発生&#10;&lt;/h3&gt;&lt;p&gt;対象リレーションのサイズが設定されている&lt;code&gt;shared_buffers&lt;/code&gt;の総容量を超えている状態で&lt;code&gt;mode =&amp;gt; &amp;lsquo;buffer&amp;rsquo;&lt;/code&gt;を実行すると、Clock Sweepアルゴリズムにより古いバッファが即座に押し出され、メモリ圧迫と不要なI/Oアロケーションが発生します。&lt;/p&gt;&#10;&lt;p&gt;&lt;b&gt;回避策&lt;/b&gt;: 実行前にリレーションサイズと現在のShared Buffers使用状況を確認し、必要に応じて&lt;code&gt;first_block&lt;/code&gt;および&lt;code&gt;last_block&lt;/code&gt;を指定して分割実行を行うか、&lt;code&gt;mode =&amp;gt; &amp;lsquo;read&amp;rsquo;&lt;/code&gt;を利用してOSページキャッシュ側に退避させます。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; pg_size_pretty(pg_relation_size(&lt;span style="color:#e6db74"&gt;&amp;#39;large_table_name&amp;#39;&lt;/span&gt;));&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SHOW&lt;/span&gt; shared_buffers;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-同期io負荷によるiops枯渇"&gt;2. 同期I/O負荷によるIOPs枯渇&#10;&lt;/h3&gt;&lt;p&gt;単一セッションで数TB規模のテーブルに&lt;code&gt;pg_prewarm&lt;/code&gt;を実行すると、ストレージ層への読み込み要求が集中し、他のトランザクションのI/Oを圧迫します。&lt;/p&gt;&#10;&lt;p&gt;&lt;b&gt;回避策&lt;/b&gt;: ブロック範囲を区切ったスクリプト化を行い、実行間隔（sleep）を挟みながら分散ロードを実施します。&lt;/p&gt;&#10;&lt;h2 id="operational-verification-protocol"&gt;Operational Verification Protocol&#10;&lt;/h2&gt;&lt;p&gt;以下は、&lt;code&gt;pg_prewarm&lt;/code&gt;実行前後のバッファロード状態を&lt;code&gt;pg_buffercache&lt;/code&gt;拡張機能を用いて検証したターミナル出力のログ例です。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ psql -U postgres -d production_db -c &amp;#34;SELECT relname, pg_size_pretty(pg_relation_size(oid)) FROM pg_class WHERE relname = &amp;#39;orders&amp;#39;;&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; relname | pg_size_pretty &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---------+----------------&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; orders | 256 MB&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(1 row)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ psql -U postgres -d production_db -c &amp;#34;SELECT count(*) FROM pg_buffercache WHERE relfilenode = pg_relation_filepath(&amp;#39;orders&amp;#39;::regclass)::name;&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; count &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-------&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 12&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(1 row)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ psql -U postgres -d production_db -c &amp;#34;SELECT pg_prewarm(&amp;#39;orders&amp;#39;, &amp;#39;buffer&amp;#39;);&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pg_prewarm &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;------------&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 32768&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(1 row)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ psql -U postgres -d production_db -c &amp;#34;SELECT count(*) FROM pg_buffercache WHERE relfilenode = pg_relation_filepath(&amp;#39;orders&amp;#39;::regclass)::name;&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; count &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-------&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 32768&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(1 row)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="configuration-notes"&gt;Configuration Notes&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;pg_prewarm&lt;/code&gt;によるバッファロードはメモリピン留めを意味しないため、読み込み後も標準のClock Sweepアルゴリズムによるページ押し出しの対象となります。&lt;/li&gt;&#10;&lt;li&gt;大規模なデータベース運用では、&lt;code&gt;autoprewarm&lt;/code&gt;によるバックグラウンド更新間隔（&lt;code&gt;pg_prewarm.autoprewarm_interval&lt;/code&gt;）の書き込みコストとデータ精度のトレードオフを検討の上でパラメータ設定を行う必要があります。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item></channel></rss>