<?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 | Systems Architecture &amp; DevOps</title><link>https://klifehack.com/en/tags/database-performance/</link><description>Recent content in Database-Performance on K-Life Hack | Systems Architecture &amp; DevOps</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Fri, 11 Sep 2026 10:30:16 +0900</lastBuildDate><atom:link href="https://klifehack.com/en/tags/database-performance/index.xml" rel="self" type="application/rss+xml"/><item><title>Preloading PostgreSQL Shared Buffers with pg_prewarm and Aurora Scaling Control</title><link>https://klifehack.com/en/p/postgresql-pg-prewarm-cache-strategy/</link><pubDate>Fri, 11 Sep 2026 10:30:16 +0900</pubDate><guid>https://klifehack.com/en/p/postgresql-pg-prewarm-cache-strategy/</guid><description>&lt;p&gt;In infrastructure operations, immediately after a database node restart or maintenance, the Shared Buffers in memory are in an initialized state (cold start). If high traffic flows in under this state, PostgreSQL must synchronously read data blocks from storage, causing disk I/O bottlenecks and significant deterioration of query latency.&lt;/p&gt;&#10;&lt;p&gt;To prevent such warm-up delays and ensure predictable I/O performance, this document outlines cache preloading techniques using the standard module &lt;code&gt;pg_prewarm&lt;/code&gt; and the operational mechanism of the &lt;code&gt;autoprewarm&lt;/code&gt; background worker available in PostgreSQL 11 and later.&lt;/p&gt;&#10;&lt;h2 id="basic-structure-and-operating-modes-of-pg_prewarm"&gt;Basic Structure and Operating Modes of pg_prewarm&#10;&lt;/h2&gt;&lt;p&gt;&lt;code&gt;pg_prewarm&lt;/code&gt; provides the functionality to load data blocks of a specified relation (table or index) directly into the OS page cache or PostgreSQL Shared Buffers.&lt;/p&gt;&#10;&lt;p&gt;As a basic setup procedure, enable the extension within the database.&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;The basic query for loading a relation into Shared Buffers is as follows:&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;The detailed signature of the &lt;code&gt;pg_prewarm&lt;/code&gt; function and the specifications of each parameter can be configured as follows:&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;Functional definition of each parameter:&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;: Specifies the target table name, index name, or 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;: Selects the memory transfer mechanism.&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;buffer&lt;/code&gt;: Loads directly into PostgreSQL Shared Buffers (default).&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;read&lt;/code&gt;: Performs synchronous reads (&lt;code&gt;read()&lt;/code&gt;) into the OS page cache without passing through Shared Buffers.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;prefetch&lt;/code&gt;: Issues asynchronous OS prefetch requests (&lt;code&gt;posix_fadvise()&lt;/code&gt;) (falls back to synchronous reads if unsupported by the OS).&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&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;: Specifies the file fork to be loaded. Specify &lt;code&gt;main&lt;/code&gt; for standard data, &lt;code&gt;fsm&lt;/code&gt; for free space map, and &lt;code&gt;vm&lt;/code&gt; for visibility map.&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;: Restricts the range of 8KB block numbers to be read. If &lt;code&gt;NULL&lt;/code&gt;, all blocks are processed.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="automatic-buffer-state-restoration-via-autoprewarm"&gt;Automatic Buffer State Restoration via autoprewarm&#10;&lt;/h2&gt;&lt;p&gt;In PostgreSQL 11 and later, the automatic restoration subsystem &lt;code&gt;autoprewarm&lt;/code&gt; is integrated into the &lt;code&gt;pg_prewarm&lt;/code&gt; extension. It records the state of Shared Buffers immediately before server shutdown and automatically reloads the relevant blocks upon restart.&lt;/p&gt;&#10;&lt;p&gt;Required configuration parameters in &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="operational-mechanism"&gt;Operational Mechanism&#10;&lt;/h3&gt;&lt;ol&gt;&#10;&lt;li&gt;&lt;b&gt;Background Dump (&lt;code&gt;autoprewarm master&lt;/code&gt;)&lt;/b&gt;:&#10;The &lt;code&gt;autoprewarm master&lt;/code&gt; worker process periodically scans the mapping information in Shared Buffers and persists the block ID list to the &lt;code&gt;$PGDATA/autoprewarm.blocks&lt;/code&gt; file (default interval: 300 seconds).&lt;/li&gt;&#10;&lt;li&gt;&lt;b&gt;Automatic Load on Startup&lt;/b&gt;:&#10;During PostgreSQL startup, a dedicated background worker reads &lt;code&gt;autoprewarm.blocks&lt;/code&gt; and sequentially reloads target blocks from disk into Shared Buffers.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="automating-replica-warm-up-in-aws-aurora-postgresql"&gt;Automating Replica Warm-up in AWS Aurora PostgreSQL&#10;&lt;/h2&gt;&lt;p&gt;In AWS Aurora PostgreSQL, a cloud-native environment, storage and compute nodes are decoupled; however, managing local Shared Buffers on each DB instance remains critical. Read Replicas added via Auto Scaling start with empty buffers immediately after launch.&lt;/p&gt;&#10;&lt;p&gt;To avoid latency increases when launching a new replica, an automated warm-up configuration integrating Amazon EventBridge and AWS Lambda is utilized.&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;[Auto Scaling Event]&#10; │&#10; ▼&#10;[Amazon EventBridge] (Detects instance creation event)&#10; │&#10; ▼&#10;[AWS Lambda Function] (Triggers SQL execution task)&#10; │&#10; ▼&#10;[Aurora Read Replica] ──&amp;amp;gt; Execute pg_prewarm() ──&amp;amp;gt; Load cache into memory&#10;&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;&#10;&lt;li&gt;Aurora Auto Scaling provisions a new target.&lt;/li&gt;&#10;&lt;li&gt;EventBridge intercepts the completion event.&lt;/li&gt;&#10;&lt;li&gt;AWS Lambda triggers and connects to the new replica node.&lt;/li&gt;&#10;&lt;li&gt;Executes &lt;code&gt;pg_prewarm&lt;/code&gt; on predefined critical table groups to populate the cache prior to traffic routing.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="troubleshooting"&gt;Troubleshooting&#10;&lt;/h2&gt;&lt;h3 id="1-occurrence-of-cache-thrashing"&gt;1. Occurrence of Cache Thrashing&#10;&lt;/h3&gt;&lt;p&gt;If &lt;code&gt;mode =&amp;gt; &amp;lsquo;buffer&amp;rsquo;&lt;/code&gt; is executed when the size of the target relation exceeds the total configured capacity of &lt;code&gt;shared_buffers&lt;/code&gt;, the Clock Sweep algorithm will immediately evict older buffers, leading to memory pressure and unnecessary I/O allocations.&lt;/p&gt;&#10;&lt;p&gt;&lt;b&gt;Workaround&lt;/b&gt;: Verify the relation size and current Shared Buffers utilization prior to execution. If necessary, execute in chunks by specifying &lt;code&gt;first_block&lt;/code&gt; and &lt;code&gt;last_block&lt;/code&gt;, or use &lt;code&gt;mode =&amp;gt; &amp;lsquo;read&amp;rsquo;&lt;/code&gt; to offload to the OS page cache instead.&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-iops-depletion-due-to-synchronous-io-load"&gt;2. IOPS Depletion Due to Synchronous I/O Load&#10;&lt;/h3&gt;&lt;p&gt;Executing &lt;code&gt;pg_prewarm&lt;/code&gt; on multi-terabyte tables within a single session concentrates read requests on the storage layer, saturating I/O for other transactions.&lt;/p&gt;&#10;&lt;p&gt;&lt;b&gt;Workaround&lt;/b&gt;: Implement scripting with partitioned block ranges and execute distributed loading with sleep intervals in between.&lt;/p&gt;&#10;&lt;h2 id="operational-verification-protocol"&gt;Operational Verification Protocol&#10;&lt;/h2&gt;&lt;p&gt;Below is an example terminal log verifying buffer loading states before and after executing &lt;code&gt;pg_prewarm&lt;/code&gt; using the &lt;code&gt;pg_buffercache&lt;/code&gt; extension.&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;Buffer loading via &lt;code&gt;pg_prewarm&lt;/code&gt; does not imply memory pinning; loaded pages remain subject to eviction by the standard Clock Sweep algorithm.&lt;/li&gt;&#10;&lt;li&gt;In large-scale database operations, parameter settings must be configured after considering the trade-off between write costs and data accuracy associated with the background update interval of &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>