<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Divyansh Gupta</title>
    <description>The latest articles on DEV Community by Divyansh Gupta (@divyansh_gupta).</description>
    <link>https://dev.to/divyansh_gupta</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1877097%2Fbb6a85b1-e31b-4cc8-b7e5-4ef21464d214.jpg</url>
      <title>DEV Community: Divyansh Gupta</title>
      <link>https://dev.to/divyansh_gupta</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/divyansh_gupta"/>
    <language>en</language>
    <item>
      <title>Optimizing Materialized View Refresh to Minimize Locks in PostgreSQL</title>
      <dc:creator>Divyansh Gupta</dc:creator>
      <pubDate>Mon, 30 Jun 2025 06:58:28 +0000</pubDate>
      <link>https://dev.to/divyansh_gupta/optimizing-materialized-view-refresh-to-minimize-locks-in-postgresql-4f76</link>
      <guid>https://dev.to/divyansh_gupta/optimizing-materialized-view-refresh-to-minimize-locks-in-postgresql-4f76</guid>
      <description>&lt;p&gt;&lt;strong&gt;Optimizing Materialized View Refresh to Minimize Locks in PostgreSQL&lt;/strong&gt;&lt;br&gt;
This article explores an enhancement to a dual‑DB PostgreSQL setup that dramatically reduces lock contention by selectively using concurrent refresh on high‑dependency materialized views. We’ve also added architecture diagrams to clarify data flows and lock behavior.&lt;/p&gt;


&lt;h3&gt;
  
  
  1. Architectural Overview
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftrypnlkl188wcvsbv192.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftrypnlkl188wcvsbv192.png" alt="Image description" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trans DB&lt;/strong&gt;: Runs non‑concurrent refreshes on most MVs, leveraging local raw data where compute is fastest.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cache DB&lt;/strong&gt;: Defines FDW-based foreign tables on Trans DB’s MVs and refreshes its own MVs concurrently, ensuring UI queries never block.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  2. Problem: Lock Explosion on High‑Dependency MV
&lt;/h3&gt;

&lt;p&gt;When &lt;code&gt;mvw_test&lt;/code&gt; (which depends on ~90% of other MVs) refreshed non‑concurrently, PostgreSQL acquires exclusive locks on each base relation. This causes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bztio5av3tb19vz0agl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bztio5av3tb19vz0agl.png" alt="Image description" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consequences&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Widespread Contention&lt;/strong&gt;: All other MVs and queries stall.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Connection Saturation&lt;/strong&gt;: Waiting sessions accumulate, exhausting slots.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Memory Spikes&lt;/strong&gt;: Queued locks consume RAM → OOM errors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Service Restarts&lt;/strong&gt;: Cache DBOOM crashes, interrupting UI.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;
  
  
  3. Solution: Selective Concurrent Refresh
&lt;/h3&gt;

&lt;p&gt;Only the high‑impact MV uses &lt;code&gt;CONCURRENTLY&lt;/code&gt;, dramatically reducing locking scope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;    &lt;span class="c1"&gt;-- Step 1: Ensure unique index&lt;/span&gt;
    &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;CONCURRENTLY&lt;/span&gt; &lt;span class="n"&gt;idx_test_pk&lt;/span&gt;
      &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;mvw_test&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_column&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;-- Step 2: Refresh concurrently&lt;/span&gt;
    &lt;span class="n"&gt;REFRESH&lt;/span&gt; &lt;span class="n"&gt;MATERIALIZED&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;CONCURRENTLY&lt;/span&gt; &lt;span class="n"&gt;mvw_test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why Concurrent?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lightweight Locks&lt;/strong&gt;: Shared locks only on the MV itself, not on its dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No Blocking&lt;/strong&gt;: Other MVs and queries continue normally.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scheduling&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Run at off‑peak, e.g., 3 AM daily via cron:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;        &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;psql&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="nv"&gt;"REFRESH MATERIALIZED VIEW CONCURRENTLY mvw_test;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  Monitor with:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;        &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;granted&lt;/span&gt;
          &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_locks&lt;/span&gt;
          &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'mvw_test'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Before vs. After: Lock Footprint
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnbahc1858xo71w6kwqe0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnbahc1858xo71w6kwqe0.png" alt="Image description" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Benefits
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduced Contention&lt;/strong&gt;: Dependencies remain unlocked.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stable Connections&lt;/strong&gt;: Fewer waiters preserve slots.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Better Memory Profile&lt;/strong&gt;: No queue‑induced OOMs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Higher Availability&lt;/strong&gt;: Cache DB stays up, UI never blocks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Focused Overhead&lt;/strong&gt;: Only one MV pays the cost of concurrency.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  6. Recommendations &amp;amp; Extensions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Index Health&lt;/strong&gt;: Periodically &lt;code&gt;REINDEX CONCURRENTLY&lt;/code&gt; the unique index.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Expand to Others&lt;/strong&gt;: Identify other MVs with &amp;gt;50% dependencies for similar treatment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Advanced&lt;/strong&gt;: Consider incremental MVs (&lt;code&gt;REFRESH MATERIALIZED VIEW ... WITH DATA&lt;/code&gt;) or logical replicas for ultra‑low‑lock reporting.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Optimizing SQL Queries with Partitioning: The Secret Weapon for Managing Massive Databases</title>
      <dc:creator>Divyansh Gupta</dc:creator>
      <pubDate>Thu, 26 Jun 2025 08:05:51 +0000</pubDate>
      <link>https://dev.to/divyansh_gupta/optimizing-sql-queries-with-partitioning-the-secret-weapon-for-managing-massive-databases-2fln</link>
      <guid>https://dev.to/divyansh_gupta/optimizing-sql-queries-with-partitioning-the-secret-weapon-for-managing-massive-databases-2fln</guid>
      <description>&lt;p&gt;In the world of data-driven applications, few things can slow down a system more than &lt;strong&gt;inefficient database queries&lt;/strong&gt;. When tables grow too large, even the most well-designed queries can become sluggish, leading to poor performance and frustrated users.&lt;br&gt;
Enter &lt;strong&gt;partitioning&lt;/strong&gt;—one of the most powerful techniques for optimizing large tables in SQL databases. In this post, we’ll dive deep into &lt;strong&gt;partitioning strategies&lt;/strong&gt;, explore &lt;strong&gt;best practices&lt;/strong&gt; for &lt;strong&gt;SQL query optimization&lt;/strong&gt;, and look at a real-world case study of a growing &lt;strong&gt;Google Docs metadata table&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;The Challenge: Performance Bottleneck in a Growing Database&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Imagine managing a table that stores metadata for over &lt;strong&gt;80 million&lt;/strong&gt; Google Drive files. Each record contains metadata such as file references, author details, creation dates, and more. As the number of records keeps climbing, you notice performance degradation in query executions. Common queries, especially those filtering by &lt;code&gt;userid&lt;/code&gt;, now take over &lt;strong&gt;30 seconds&lt;/strong&gt; to execute. The growing database volume is overwhelming your queries, and traditional optimization methods no longer cut it.&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Why Partitioning is the Key to Query Optimization&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;At the heart of the performance issue lies the fact that a single, massive table is being queried for large amounts of data. &lt;strong&gt;Partitioning&lt;/strong&gt;—the process of dividing a large table into smaller, more manageable pieces—can dramatically improve query performance. It allows the database to operate on these smaller subsets, reducing the time needed to scan and retrieve relevant records.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;How Partitioning Works&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When we partition a table, we divide it into smaller, logically separate &lt;strong&gt;partitions&lt;/strong&gt; based on a chosen &lt;strong&gt;partition key&lt;/strong&gt;. In our case, partitioning by &lt;code&gt;userid&lt;/code&gt; makes sense because it’s a &lt;strong&gt;mandatory field&lt;/strong&gt; in almost all queries, and it directly maps to how users interact with their data. This leads to &lt;strong&gt;partition pruning&lt;/strong&gt;, where only the relevant partitions are scanned for the data that the query needs.&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Partitioning Strategy for Google Docs Metadata Table&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let’s go step by step to explore the partitioning strategy we used for optimizing the &lt;code&gt;dbo.googledocs_tbl&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Partition Key Selection&lt;/strong&gt;: We selected &lt;strong&gt;&lt;code&gt;userid&lt;/code&gt;&lt;/strong&gt; as the partition key because it is commonly used in queries and is essential for filtering data specific to individual users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hash Partitioning&lt;/strong&gt;: To ensure uniform data distribution across partitions, we opted for &lt;strong&gt;hash partitioning&lt;/strong&gt;. This technique spreads data across partitions evenly, minimizing the risk of data skew where some partitions might hold more data than others.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Number of Partitions&lt;/strong&gt;: Based on our analysis and volume of data, we created &lt;strong&gt;74 partitions&lt;/strong&gt;. This ensures even distribution of user data while providing ample room for future growth.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Targeted Indexing&lt;/strong&gt;: We designed &lt;strong&gt;partition-specific indexes&lt;/strong&gt; to ensure that search operations for individual partitions remain fast and efficient. For instance, indexes on columns like &lt;code&gt;docfileref&lt;/code&gt;, &lt;code&gt;authoremail&lt;/code&gt;, and &lt;code&gt;createddate&lt;/code&gt; are optimized for the specific partition data.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Before and After: A Tale of Query Performance&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let’s visualize the &lt;strong&gt;before and after&lt;/strong&gt; performance when partitioning is applied to our queries. We'll use a real-world example to see how partitioning improves query execution.&lt;br&gt;
&lt;strong&gt;Before Partitioning:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Imagine you are running a query that searches for all files created by &lt;code&gt;user123&lt;/code&gt; between &lt;code&gt;2024-01-01&lt;/code&gt; and &lt;code&gt;2024-12-31&lt;/code&gt;. The query has to scan millions of rows, filtering based on multiple columns like &lt;code&gt;docfileref&lt;/code&gt;, &lt;code&gt;createddate&lt;/code&gt;, and &lt;code&gt;userid&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docs_tbl&lt;/span&gt; 
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;userid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'user123'&lt;/span&gt; 
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;createddate&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="s1"&gt;'2024-01-01'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="s1"&gt;'2024-12-31'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the query has to scan &lt;strong&gt;all&lt;/strong&gt; the rows in the table (even those that don’t match the &lt;code&gt;userid&lt;/code&gt; filter), leading to &lt;strong&gt;slow performance&lt;/strong&gt; and a longer wait time for results.&lt;br&gt;
&lt;strong&gt;After Partitioning:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
With partitioning, the query now targets only the &lt;strong&gt;relevant partition&lt;/strong&gt; for &lt;code&gt;user123&lt;/code&gt;. The query becomes far more efficient, scanning a much smaller dataset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docs_tbl_ptn_part_1&lt;/span&gt; 
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;userid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'user123'&lt;/span&gt; 
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;createddate&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="s1"&gt;'2024-01-01'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="s1"&gt;'2024-12-31'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The table has been partitioned by &lt;code&gt;userid&lt;/code&gt;, and now the query only needs to scan the partition containing &lt;code&gt;user123&lt;/code&gt;'s data, resulting in a &lt;strong&gt;dramatic reduction in execution time&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Partitioning in Action: Real-World Examples of Query Optimization&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let's explore some &lt;strong&gt;real-world scenarios&lt;/strong&gt; where partitioning significantly improves query performance.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;1. Accessing User Archives&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Before partitioning, fetching a user’s archived documents would require scanning the entire table, even though we are only interested in one user’s data. With partitioning, queries can skip irrelevant data and directly access the data for the specific &lt;code&gt;userid&lt;/code&gt;.&lt;br&gt;
&lt;strong&gt;Query Before Partitioning&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docs_tbl&lt;/span&gt; 
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;userid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'user123'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;retentionstatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Query After Partitioning&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docs_tbl_ptn_part_1&lt;/span&gt; 
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;userid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'user123'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;retentionstatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;2. Optimizing Aggregation Queries&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Aggregation queries like calculating the count of documents or average file sizes can be slow without partitioning, as they scan the entire table. Partitioning allows us to perform aggregation on individual partitions, making these queries much faster.&lt;br&gt;
&lt;strong&gt;Query Before Partitioning&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docs_tbl&lt;/span&gt; 
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;createddate&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="s1"&gt;'2024-01-01'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="s1"&gt;'2024-12-31'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Query After Partitioning&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docs_tbl_ptn_part_1&lt;/span&gt; 
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;createddate&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="s1"&gt;'2024-01-01'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="s1"&gt;'2024-12-31'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;Best Practices for Indexing Partitioned Tables&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;While partitioning helps to reduce the data scanned by queries, &lt;strong&gt;indexing&lt;/strong&gt; plays a crucial role in improving query performance within each partition.&lt;br&gt;
Here’s a set of &lt;strong&gt;best practices&lt;/strong&gt; for creating indexes on partitioned tables:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Local Indexes&lt;/strong&gt;: Local indexes are specific to each partition, making them more efficient than global indexes, which span the entire table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Index Columns Frequently Filtered&lt;/strong&gt;: Focus on creating indexes for columns that are frequently used in filters, such as &lt;code&gt;userid&lt;/code&gt;, &lt;code&gt;docfileref&lt;/code&gt;, and &lt;code&gt;createddate&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimize Full-Text Search&lt;/strong&gt;: For text-heavy queries, consider using a &lt;strong&gt;GIN index&lt;/strong&gt; for columns that are often searched via full-text searches, such as &lt;code&gt;title&lt;/code&gt; or &lt;code&gt;description&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;    &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;ix_docs_tbl_title&lt;/span&gt; 
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docs_tbl&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;gin_trgm_ops&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;Step-by-Step Migration Plan: From Single Table to Partitioned Table&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Migrating to a partitioned table requires careful planning. Below is a streamlined migration plan:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create Partitioned Parent Table&lt;/strong&gt;: Create the new partitioned table that mirrors the existing table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create Partitions&lt;/strong&gt;: Create 74 child partitions using a hash function based on &lt;code&gt;userid&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create Indexes&lt;/strong&gt;: Set up indexes to optimize search and retrieval on the partitioned table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Migration&lt;/strong&gt;: Migrate data in batches to avoid locking the entire table. Monitor progress using a &lt;strong&gt;migration tracking table&lt;/strong&gt; (&lt;code&gt;partition_migration_tbl&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Switch Over&lt;/strong&gt;: After data migration is complete, rename the partitioned table to take over the production role.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Monitoring Migration: Visualizing Progress with Grafana&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Real-time monitoring during migration is critical for success. &lt;strong&gt;Grafana&lt;/strong&gt; provides an excellent way to monitor progress, ensuring the migration is on track. By querying the &lt;strong&gt;migration tracking table&lt;/strong&gt; (&lt;code&gt;partition_migration_tbl&lt;/code&gt;), Grafana can visualize key metrics like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data migration status&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Index creation progress&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Overall migration completion&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Query latency before and after partitioning&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Example Grafana Dashboard for Migration&lt;/strong&gt;:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bar chart&lt;/strong&gt;: Visualizes migration progress across partitions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Line graph&lt;/strong&gt;: Tracks query performance improvements over time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alerting&lt;/strong&gt;: Set up notifications if migration slows down or encounters errors.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;PoC Results: How Partitioning Improves Query Performance&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In our &lt;strong&gt;Proof of Concept&lt;/strong&gt; (PoC), we tested partitioning with two datasets: &lt;strong&gt;16 million records&lt;/strong&gt; and &lt;strong&gt;1.6 billion records&lt;/strong&gt;. The results were striking:&lt;br&gt;
| &lt;strong&gt;Data Volume&lt;/strong&gt; | &lt;strong&gt;Query Time (Without Partitioning)&lt;/strong&gt; | &lt;strong&gt;Query Time (With Partitioning)&lt;/strong&gt; |&lt;br&gt;
| --- | --- | --- |&lt;br&gt;
| 16 Million | 17 seconds, 30,000 disk reads | 0.1 seconds, 207 disk reads |&lt;br&gt;
| 1.6 Billion | 400 seconds, 550,000 disk reads | 2 seconds, 7,500 disk reads |&lt;br&gt;
As the table shows, partitioning not only &lt;strong&gt;dramatically reduces query time&lt;/strong&gt; but also reduces the &lt;strong&gt;disk I/O&lt;/strong&gt; significantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Mermaid Diagram: Migration Process Flow&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Here’s a &lt;strong&gt;Mermaid diagram&lt;/strong&gt; to visualize the migration process of partitioning:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya15u1q9rrk5hbyjyiex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya15u1q9rrk5hbyjyiex.png" alt="Image description" width="800" height="3157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This &lt;strong&gt;Mermaid diagram&lt;/strong&gt; illustrates the &lt;strong&gt;step-by-step migration process&lt;/strong&gt;, ensuring that the entire migration flow is smooth and that the database remains consistent throughout.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion: Embrace Partitioning for Long-Term Scalability&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Partitioning is a &lt;strong&gt;game-changing strategy&lt;/strong&gt; for optimizing SQL queries, especially for large datasets. By splitting large tables into smaller, more manageable partitions, you significantly improve query performance, reduce disk I/O, and ensure your database remains &lt;strong&gt;scalable&lt;/strong&gt; as data grows.&lt;br&gt;
For &lt;strong&gt;Database Administrators&lt;/strong&gt;, partitioning offers the opportunity to &lt;strong&gt;future-proof&lt;/strong&gt; their systems while providing a seamless experience for users. Combine partitioning with &lt;strong&gt;strategic indexing&lt;/strong&gt;, &lt;strong&gt;real-time monitoring&lt;/strong&gt; with &lt;strong&gt;Grafana&lt;/strong&gt;, and a careful migration plan, and you’ll have a &lt;strong&gt;well-optimized database&lt;/strong&gt; that can handle even the largest data volumes without breaking a sweat.&lt;br&gt;
If you're dealing with massive data tables, partitioning isn't just a &lt;strong&gt;best practice&lt;/strong&gt;—it’s essential for keeping your database &lt;strong&gt;fast, scalable&lt;/strong&gt;, and &lt;strong&gt;user-friendly&lt;/strong&gt;. Embrace partitioning and watch your query performance soar!&lt;/p&gt;




&lt;p&gt;This enhanced blog should now serve as a comprehensive guide, with more &lt;strong&gt;creative insights&lt;/strong&gt;, &lt;strong&gt;detailed explanations&lt;/strong&gt;, and &lt;strong&gt;real-world applications&lt;/strong&gt; of partitioning in SQL. It provides clear examples and practical steps, making it a valuable resource for database administrators looking to improve performance in large-scale databases.&lt;/p&gt;

</description>
      <category>partition</category>
      <category>database</category>
      <category>postgressql</category>
      <category>dba</category>
    </item>
    <item>
      <title>Database optimization best practices</title>
      <dc:creator>Divyansh Gupta</dc:creator>
      <pubDate>Wed, 25 Jun 2025 10:48:52 +0000</pubDate>
      <link>https://dev.to/divyansh_gupta/database-optimization-best-practices-3p2d</link>
      <guid>https://dev.to/divyansh_gupta/database-optimization-best-practices-3p2d</guid>
      <description>&lt;p&gt;Imagine your database as a wild animal sanctuary: some queries lumber like tortoises, while others sprint like cheetahs. Your job as a DBA is to coax every query into channeling its inner cheetah—fast, efficient, and resource-savvy. In this KB article, you’ll discover practical techniques, vibrant code examples, ASCII-art execution plans, and &lt;strong&gt;Mermaid&lt;/strong&gt; flowcharts that transform sluggish SQL into scalpels of performance. fileciteturn0file0&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Measure Twice, Cut Once: EXPLAIN &amp;amp; ANALYZE
&lt;/h2&gt;

&lt;p&gt;Before refactoring, know your enemy. Use:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXPLAIN (ANALYZE, BUFFERS)
SELECT ...;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;to expose hidden bottlenecks: row estimates, buffer hits vs. reads, and CPU vs. I/O costs.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                     QUERY PLAN
-----------------------------------------------------------------------------------
 Hash Join  (cost=150.00..500.00 rows=1000 width=64) (actual time=12.345..45.678 rows=950 loops=1)
   Hash Cond: (t1.id = t2.foreign_id)
   Buffers: shared hit=2000 read=1500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This tells you whether your query is CPU-bound, I/O-bound, or suffering from bad cardinality estimates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6q3obe6pamu0tdhdt51.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6q3obe6pamu0tdhdt51.png" alt="Image description" width="800" height="1020"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Indexing Mastery: More Than Just B‑Trees
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Partial &amp;amp; Expression Indexes
&lt;/h3&gt;

&lt;p&gt;Target hot filter patterns without bloating:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE INDEX idx_active_users ON users((lower(email)))
 WHERE status = 'active';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  2.2 BRIN for Time-Series
&lt;/h3&gt;

&lt;p&gt;Massive append-only tables? Try BRIN:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE logs (
  ts TIMESTAMPTZ,
  event JSONB
) PARTITION BY RANGE (ts);
CREATE INDEX ON logs USING BRIN (ts);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This lightweight index slashes size at scale.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Encapsulate Complexity: Stored Functions &amp;amp; Views
&lt;/h2&gt;

&lt;p&gt;Rather than embedding 10 JOINs in every API call, wrap logic in a function or view:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE OR REPLACE FUNCTION daily_sales_summary(day DATE)
RETURNS TABLE(user_id UUID, total DECIMAL) AS $$
BEGIN
  RETURN QUERY
  SELECT s.user_id, SUM(amount)
  FROM sales s
  WHERE date_trunc('day', s.ts) = day
  GROUP BY s.user_id;
END;
$$ LANGUAGE plpgsql;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The planner can optimize a stable function more aggressively than ad-hoc SQL.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Aggregations &amp;amp; Windows: Tricks of the Trade
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Materialized Aggregates
&lt;/h3&gt;

&lt;p&gt;For metrics dashboards, precompute:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE MATERIALIZED VIEW mv_user_errors AS
SELECT user_id, COUNT(*) AS error_count
FROM events
WHERE error_flag
GROUP BY user_id;
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_user_errors;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  4.2 Window Functions vs. GROUP BY
&lt;/h3&gt;

&lt;p&gt;When you need both raw rows and aggregates:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
  order_id,
  amount,
  SUM(amount) OVER (PARTITION BY customer_id) AS total_per_customer
FROM orders;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Use an index on &lt;code&gt;(customer_id, amount)&lt;/code&gt; to speed windows.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Partitioning &amp;amp; Parallelism: Scale Out Safely
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Declarative Partitioning
&lt;/h3&gt;

&lt;p&gt;Split by time or key:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE metrics (
  ts DATE,
  value DOUBLE PRECISION
) PARTITION BY RANGE (ts);
CREATE TABLE metrics_2025_q1 PARTITION OF metrics
 FOR VALUES FROM ('2025-01-01') TO ('2025-04-01');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  5.2 Harness Parallel Queries
&lt;/h3&gt;

&lt;p&gt;Enable in &lt;code&gt;postgresql.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;max_parallel_workers_per_gather = 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then large scans auto-split across CPUs.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Housekeeping: VACUUM, ANALYZE &amp;amp; Maintenance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6.1 Autovacuum Tuning
&lt;/h3&gt;

&lt;p&gt;Ensure &lt;code&gt;autovacuum&lt;/code&gt; thresholds fit your workload. For high-churn tables:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALTER TABLE big_table
SET ( autovacuum_vacuum_scale_factor = 0.05,
      autovacuum_analyze_scale_factor = 0.02 );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  6.2 Fillfactor for Write-Heavy Tables
&lt;/h3&gt;

&lt;p&gt;Reserve free space to reduce page splits:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALTER TABLE logs SET (fillfactor = 70);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;h2&gt;
  
  
  7. Real‑World Case Study: 80% Speedup
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; A nightly report took 10 minutes. By applying:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Partial index on &lt;code&gt;status&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Function-based view&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Partition pruning on date&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Autovacuum tuning&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;we tracked its execution plan changes:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Before:
Seq Scan on orders  (time: 600s)
-- After:
Index Only Scan using idx_status_date  (time: 120s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;From 10 min → 2 min: a success story to inspire your own triumphs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Measure first&lt;/strong&gt; with &lt;code&gt;EXPLAIN ANALYZE (BUFFERS)&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Index smartly&lt;/strong&gt;: partial, expression, BRIN.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Encapsulate&lt;/strong&gt; complex logic in functions/views.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Precompute&lt;/strong&gt; heavy aggregates with materialized views.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Partition &amp;amp; parallelize&lt;/strong&gt; for scale.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintain&lt;/strong&gt;: VACUUM, ANALYZE, and fillfactor.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8. Beyond the Basics: Advanced Techniques
&lt;/h2&gt;

&lt;h3&gt;
  
  
  8.1 Adaptive Query Plans with pg_stat_statements
&lt;/h3&gt;

&lt;p&gt;Track your most expensive statements:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE EXTENSION pg_stat_statements;
SELECT query, calls, total_time, mean_time
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 5;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Use this insight to prioritize optimizations.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.2 Plan Stability with Prepared Statements
&lt;/h3&gt;

&lt;p&gt;For queries with variable patterns, prepared statements lock in good plans:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PREPARE fast_search(text) AS
SELECT * FROM products WHERE description ILIKE $1;
EXECUTE fast_search('%widget%');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  8.3 In-Memory Speed with UNLOGGED Tables
&lt;/h3&gt;

&lt;p&gt;Temp-heavy data can live in RAM:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE UNLOGGED TABLE temp_hits AS
SELECT ...;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  8.4 Smart Caching Layers
&lt;/h3&gt;

&lt;p&gt;Combine Redis or PGSQL's native caching:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DO $$
BEGIN
  PERFORM pg_prewarm('hot_table');
END;
$$;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;h2&gt;
  
  
  Creative Corner: Visualizing Data Flow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnbhmohri4onhtzqdvm1c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnbhmohri4onhtzqdvm1c.png" alt="Image description" width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bring your diagrams to life—they guide both your brain and your team.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts: The Art of Performance
&lt;/h2&gt;

&lt;p&gt;Optimizing SQL is equal parts science and art. It’s a continuous journey: measure, tweak, observe, and repeat. With these techniques—from core index strategies to creative caching and plan management—you’re equipped to turn any tortoise into a cheetah.&lt;br&gt;
Remember: the fastest query is the one you never run. Cache wisely, precompute where it counts, and let your database shine.&lt;/p&gt;

</description>
      <category>postgressql</category>
      <category>sql</category>
      <category>database</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
