Postgres Performance Tuning Query Plans

As your data scales and your applications become more complex, maintaining the responsiveness and efficiency of your database system is paramount. Postgres, a renowned open source relational database, has risen to prominence as a cornerstone for many businesses, including the technology powerhouse GOJEK, which relies on it to power its diverse array of services within its Super App. Despite its robust features and capabilities, Postgres is not impervious to slowdowns, particularly as the quantity of data swells. Addressing performance issues is essential, not just for start-ups like GOJEK but for any enterprise seeking to optimise their systems for better performance.

Optimising the performance of a relational database like Postgres starts with understanding its planner or optimizer. This component is crucial as it devises the most efficient route for query execution, acting as the database’s ‘brain’. Utilising inbuilt tools such as ‘EXPLAIN’ and ‘ANALYZE’, you can delve into the nuances of query plans and unlock potential optimisations. This process can lead to substantial improvements in database performance, ensuring that your applications run smoothly and reliably, even at scale.

Key Takeaways

  • Postgres, an open source relational database, is crucial for businesses like GOJEK, operating at scale.
  • Database performance can be optimised by understanding and leveraging the database’s query execution plans.
  • Tools like ‘EXPLAIN’ and ‘ANALYZE’ are instrumental in dissecting and enhancing database operations.


EXPLAIN provides a detailed breakdown of your SQL query’s anticipated path, factoring in table statistics and database structures like indexes. It predicts the PostgreSQL planner’s course of action without executing the query.

Contrastingly, ANALYZE actually runs the query, measuring actual execution time. It’s instrumental for:

  • Unveiling the execution plan steps, revealing whether a sequential (seq) scan or another method is used.
  • Gauging query processor efficiency by timing the operations.

Together, EXPLAIN and ANALYZE serve to enhance database performance by optimising query execution through informative insights about planned and actual processing times.

Unveiling the Structure

In enhancing your PostgreSQL database’s efficiency, understanding the execution plan is paramount. When you direct PostgreSQL to detail the execution plan for a query such as EXPLAIN SELECT * FROM schemes;, you witness a sequential scan— PostgreSQL iterates through each row, indicating the potential cost associated.

Components Impacting Cost Calculation:

  • Disk Read Pages: Quantity of pages read from the disk.
  • Rows Scanned: Number of rows evaluated.
  • Seq Page Cost: Cost estimate for each disk page read.
  • CPU Tuple Cost: Cost estimate for processing each individual row.

For instance, calculated as (Disk Read Pages x Seq Page Cost) + (Rows Scanned x CPU Tuple Cost), a simple cost approximation might equal (54 x 1.0) + (1000 x 0.01) = 64.

When filters augment a query—for instance, querying only ‘active’ schemes—the cost naturally escalates, reflecting the two-step process where all rows are initially considered and subsequently narrowed down by the specified conditions. This informed approach to performance tuning, along with utilising PostgreSQL performance tuning tools, can lead to augmented query performance.

Adjusting Database Performance Factors

When configuring your PostgreSQL database, it’s crucial to understand the role of planner cost constants. These constants are central to the query planner’s decision-making process and are set in relation to the cost of fetching a page sequentially, known as seq_page_cost. The default for this is typically 1.0, and other costs are relative to this figure.

Here’s a concise guide to tweaking these parameters:

  • Sequential Page Cost (seq_page_cost): Default is 1.0; a baseline for other costs.
  • Random Page Cost (random_page_cost): Often higher than seq_page_cost due to the additional overhead of random access.

Resource limits can also impact planning considerations:

  • Working Memory (work_mem): Allocated memory for query operations like sorts or hashes.
  • Connection Limit (max_connections): The number of concurrent connections your database can handle.
  • Write-Ahead Log Buffers (wal_buffers): Dedicated memory for write-ahead logs.

To tweak performance further:

  • Shared Buffers (shared_buffers): Generally set to 25% of available memory.
  • Effective Cache Size (effective_cache_size): Informs the planner on available memory for caching.
  • CPU Tuple Cost (cpu_tuple_cost): Estimates the CPU cost to process each row.

IT professionals must proceed with caution when adjusting these constants. They are averages intended to reflect the diverse range of queries. Consequently, altering them without a comprehensive understanding of their overall impact can lead to suboptimal performance.

Maintaining a balanced database system occasionally requires optimisation of maintenance settings:

  • Maintenance Work Memory (maintenance_work_mem): Memory for maintenance tasks like VACUUM and CREATE INDEX.

When you encounter complex queries, the planner might opt for a multi-step approach to streamline data retrieval, improving efficiency.

Remember, these metrics aren’t merely numbers; they are leveraged to harmoniously balance system resources and workloads. Adjust them thoughtfully to enhance your database’s performance.

Delving Into the Details

When dealing with a SQL database, understanding how data is retrieved can be critical to enhance performance. Your database operations can be categorised into two distinct scanning methods:

  • Bitmap Scans: Imagine having a strategic method for your database to identify the necessary disk pages. By using a conjunction of indexes, bitmap scans efficiently access only the pages required, avoiding excessive disk activity.
  • Sequential Scans: Occasionally, a simpler approach might outpace more complex methods. Sequentially reading disk pages can outperform index-based methods, especially when the data on disk is organised in a contiguous block.

It is worthwhile noting that these methods are influenced by the underlying hardware characteristics. The disk I/O, for instance, has a pronounced impact:

  • Random vs Sequential I/O: Sequential reads generally best random reads due to the nature of disk mechanics, making them apt for extensive data retrieval.

When implementing filters within your queries, using WHERE clauses wisely is crucial:

  • Index Scans: While they can be optimal for less frequent matches, they hinge on multiple I/O operations—consulting the index, then fetching the targeted row from the RAM.

In scenarios where you notice your database vacuuming often or you experience bloat, consider hardware updates like augmenting RAM or expanding disk space. Monitoring your logs can reveal slow queries, unlocking opportunities to refine schemas or adjust caching strategies.

Lastly, your operating system and network setup—taking into account network cards and nodes—play a pivotal role in data communication. Opt for a robust two-step plan, tackling both software optimisation and hardware enhancements to reduce slow-running queries and overall system lag.

Query Plan on Joins

When working with SQL queries that involve more than one table, the query optimiser plays a crucial role in defining how the tables are brought together. There are different join algorithms that PostgreSQL might utilise, each appropriate for certain conditions.

  • Nested Loop: This method considers every combination of rows from two tables. When one table is significantly smaller, the engine typically performs a sequential scan. If not, and an index is available, an index scan is more likely to be employed.
  • Hash Join: Should the query involve a large and a small table, a hash join could be the go-to approach. The process begins by hashing the smaller table based on the join key, creating a hash table. This table is then used to efficiently locate matching rows in the larger table as it is scanned.
  • Merge Join: Think of merge sort when envisioning merge join. This method requires both tables to be ordered by the join key. Once sorted, they are traversed in tandem to locate pairs of rows with the join condition satisfied.

Understanding the intricacies of these algorithms is pivotal to optimising your queries. Often, PostgreSQL’s EXPLAIN/ANALYZE statements are instrumental in uncovering which join type is being used—shedding light on the operational aspects of your query.

PostgreSQL tuning can be nuanced since there’s no one-size-fits-all solution; the specific circumstances of a query determine the optimisation strategy. To enhance your database’s performance, it’s essential to recognise and rectify issues by delving into PostgreSQL’s various tools and understanding the underlying statistics.

To discover more on how to apply this knowledge for performance tuning, consider reading on the Postgres performance tuning methods. Engaging with the complexities of PostgreSQL can empower you to fine-tune your database effectively.

Enhancing Query Performance with Indexes

Accelerating Postgres Queries Using Indexes

When aiming to reduce the duration of query execution in Postgres, indexes play a crucial role. By applying indexes effectively, your database can avoid full table scans, instead swiftly locating the rows needed.

  • Primary Key: Ensure your table’s primary key is indexed, facilitating fast direct lookups.
  • Unique Constraints: Employ unique constraints to prevent duplicate records, which inherently creates a unique index.
  • Index Types: Utilize different types of indexes, such as bitmap scans for low cardinality columns, that match your query patterns.
  • Index Scan vs Bitmap Heap Scan: Use index scans for direct row retrieval and bitmap heap scans when dealing with multiple rows matching a condition.

Remember, not all indexes improve performance; unused indexes can lead to unnecessary overhead. Regularly monitor and prune indexes that aren’t actively benefiting your query times.

Frequently Asked Questions

Enhancing Performance of SELECT Queries in PostgreSQL

To improve the execution speed of your SELECT queries in PostgreSQL, consider indexing pertinent columns, especially those regularly used in WHERE clauses. Additionally, refining your query design and employing EXPLAIN to understand query plans are crucial steps. Remember to regularly VACUUM and ANALYSE your database to keep statistics up-to-date and plan choices optimal.

Tuning Methods for PostgreSQL Query Execution Plans

Effective tuning of PostgreSQL’s query execution plans includes, but is not limited to:

  • Utilising the EXPLAIN command to interpret and correct inefficient plans.
  • Adjusting planner cost constants such as seq_page_cost and random_page_cost based on system performance and storage setup.
  • Restricting overly broad searches with more selective query constraints.

Learn about query plans to better identify areas for tuning.

Visualising PostgreSQL Query Plans

For visualising query plans in PostgreSQL:

  • Tools like pgAdmin provide graphical representations.
  • Online services can turn EXPLAIN output into diagrams for easier interpretation.

These visual aids can help you pinpoint performance bottlenecks within complex queries.

Impact of plan_cache_mode Setting on Performance

The plan_cache_mode setting determines caching of query plans:

  • auto (the default) enables caching based on cost estimates and query characteristics.
  • force_custom_plan always generates new custom plans, bypassing the cache.
  • force_generic_plan always uses generic plans, ideal for stable environments where plan variance is minimal.

Correctly configuring plan_cache_mode can significantly affect the performance of repeated queries.

Significance of effective_cache_size in Query Optimisation

The effective_cache_size parameter informs the PostgreSQL planner about the memory available for caching data:

  • A well-considered value helps the planner to more accurately predict the cost of index scans versus sequential scans.
  • It doesn’t allocate memory, but estimates how much memory is available for disk caching.

Adjusting effective_cache_size can lead to better decision-making in the planning process of queries.

Best Practices for Update Query Optimisation

To ensure update query performance in PostgreSQL is optimal:

  • Batch your updates to reduce transaction overheads.
  • Use indexes to quickly locate rows for updating, but be cautious of indexing overhead.
  • Limit the width of updated rows to conserve space and maintain efficiency.

Incorporating such best practices can significantly enhance update operations within your PostgreSQL database.

Leave a Comment