Softechinfra
Development

PostgreSQL Performance Tuning: Complete Optimization Guide

Optimize PostgreSQL with configuration tuning, indexing strategies, query optimization, and monitoring. Performance best practices.

Hrishikesh BaidyaHrishikesh Baidya
October 12, 202211 min read
PostgreSQL Performance Tuning: Complete Optimization Guide

PostgreSQL is powerful but requires tuning for optimal performance. Our development team at Softechinfra shares techniques to make your database faster.

95%+
Target Cache Hit
10x
Query Speedup
25%
RAM for shared_buffers
#4
Most Popular DB

Understanding Performance

⏱️ Query Execution

Measure query execution time with EXPLAIN ANALYZE

📊 Throughput

Track queries/second and concurrent connections

💾 Cache Ratio

Monitor buffer cache hit ratio (target 95%+)

💿 Disk I/O

Minimize disk reads with proper indexing

Identifying Issues

pg_stat_statements

sql
SELECT query, calls, total_time, mean_time
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 10;

EXPLAIN ANALYZE

sql
EXPLAIN ANALYZE
SELECT * FROM orders
WHERE customer_id = 123
AND created_at > '2022-01-01';

Configuration Tuning

Memory Settings

code
# postgresql.conf

# Shared memory buffer
shared_buffers = 25% of RAM (e.g., 4GB for 16GB RAM)

# Work memory per operation
work_mem = 64MB

# Maintenance operations
maintenance_work_mem = 1GB

# Effective cache size (OS cache estimate)
effective_cache_size = 75% of RAM

Connection Settings

code
# Maximum connections
max_connections = 200

# Consider using PgBouncer for connection pooling

Write Performance

code
# WAL settings
wal_buffers = 64MB
checkpoint_completion_target = 0.9
max_wal_size = 4GB

Indexing Strategies

Index Types

B-tree (default)

sql
CREATE INDEX idx_orders_customer
ON orders (customer_id);

Partial Index

sql
CREATE INDEX idx_orders_pending
ON orders (created_at)
WHERE status = 'pending';

Composite Index

sql
CREATE INDEX idx_orders_customer_date
ON orders (customer_id, created_at DESC);

GIN Index (for JSONB)

sql
CREATE INDEX idx_users_metadata
ON users USING GIN (metadata);

Index Maintenance

sql
-- Check index usage
SELECT indexrelname, idx_scan, idx_tup_read
FROM pg_stat_user_indexes;

-- Reindex
REINDEX INDEX idx_orders_customer;

-- Remove unused indexes
DROP INDEX unused_index;

Query Optimization

Common Patterns

Use EXISTS instead of IN

sql
-- Slow
SELECT * FROM orders
WHERE customer_id IN (SELECT id FROM customers WHERE active);

-- Faster
SELECT * FROM orders o
WHERE EXISTS (
  SELECT 1 FROM customers c
  WHERE c.id = o.customer_id AND c.active
);

Avoid SELECT *

sql
-- Bad
SELECT * FROM orders;

-- Good
SELECT id, customer_id, total FROM orders;

Use LIMIT for pagination

sql
-- With cursor-based pagination
SELECT * FROM orders
WHERE id > :last_id
ORDER BY id
LIMIT 20;

Join Optimization

  • Ensure join columns are indexed
  • Filter early
  • Use appropriate join types
  • Table Maintenance

    VACUUM

    sql
    -- Regular vacuum
    VACUUM ANALYZE orders;
    
    -- Full vacuum (blocks table)
    VACUUM FULL orders;

    Auto-vacuum Tuning

    code
    autovacuum_vacuum_scale_factor = 0.1
    autovacuum_analyze_scale_factor = 0.05

    Table Partitioning

    sql
    CREATE TABLE orders (
      id SERIAL,
      created_at TIMESTAMP,
      ...
    ) PARTITION BY RANGE (created_at);
    
    CREATE TABLE orders_2022
    PARTITION OF orders
    FOR VALUES FROM ('2022-01-01') TO ('2023-01-01');

    Connection Pooling

    PgBouncer

    Configuration

    code
    [databases]
    mydb = host=localhost dbname=mydb
    
    [pgbouncer]
    pool_mode = transaction
    max_client_conn = 1000
    default_pool_size = 20
      Benefits
    • Reduce connection overhead
    • Handle many clients
    • Improve performance

    Monitoring

    Key Queries

    Cache hit ratio

    sql
    SELECT
      sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) as ratio
    FROM pg_statio_user_tables;

    Long running queries

    sql
    SELECT pid, now() - pg_stat_activity.query_start AS duration, query
    FROM pg_stat_activity
    WHERE state != 'idle'
    ORDER BY duration DESC;

    Tools

  • pg_stat_statements
  • pgAdmin
  • DataDog / New Relic
  • pgBadger (log analysis)
  • Conclusion

    "PostgreSQL performance tuning combines configuration, indexing, query optimization, and maintenance. Monitor continuously and optimize iteratively."
    — Hrishikesh Baidya, Lead Developer

    Database performance is critical for application responsiveness. We've optimized PostgreSQL databases for projects like AppliedView and Radiant Finance using our backend development expertise. See our database migration guide for more.

    Need Database Performance Optimization?

    Our team provides database consulting, performance tuning, and optimization services for PostgreSQL and other databases.

    Get Database Assessment
    Tags:
    PostgreSQLDatabasePerformanceBackendOptimization
    Share this post:
    Hrishikesh Baidya

    Hrishikesh Baidya

    CTO at Softechinfra specializing in Python, system architecture, and building secure, scalable software solutions.