Tuning SQL queries is only part of the performance story.
To get the best out of PostgreSQL, you also need to tune the database server and operating system.
In this article, weβll explore the key aspects of performance tuning, from hardware considerations to PostgreSQL configuration parameters.
Why Performance Tuning Matters
Even well-written queries can suffer if:
- The server is misconfigured.
- Hardware resources are not optimized.
- Too many connections overwhelm the system.
- Checkpoints and WAL settings are poorly tuned.
π Performance tuning ensures PostgreSQL uses your hardware efficiently and scales smoothly.
Step 1: Start with the Operating System
Before tweaking PostgreSQL, verify that the OS is not the bottleneck:
- Memory: Ensure enough RAM is available to avoid swapping.
- CPU: Monitor load and wait times.
- Disk I/O: Check for latency spikes.
Best practices:
- Put transaction logs (
pg_wal
) on a dedicated disk. - Use RAID 10 (0+1) for speed and redundancy.
- Avoid remote file systems for data directories.
- Disable unnecessary filesystem overhead (e.g., mount with
noatime
).
Step 2: Tune PostgreSQL Parameters
PostgreSQLβs default settings are conservative.
Adjust these in postgresql.conf:
Connection Settings
- max_connections: Default is often too high. Use a connection pooler (PgBouncer/pgpool-II) instead of increasing this blindly.
Memory Settings
- shared_buffers: Typically set to 25β40% of available RAM.
- work_mem: Memory per query operation (sorts, hashes). Tune carefully to avoid excessive memory usage.
- maintenance_work_mem: Used for VACUUM, CREATE INDEX. Increase for faster maintenance.
- effective_cache_size: Helps planner estimate available cache (usually 50β75% of RAM).
WAL & Checkpoints
- wal_level: Use
replica
for replication,logical
for logical decoding. - wal_buffers: Auto-tuned, but can be increased for write-heavy systems.
- max_wal_size: Increase to reduce checkpoint frequency.
- checkpoint_timeout: Default 5 minutes. Increasing reduces checkpoint pressure.
Step 3: Use Parallel Queries
PostgreSQL supports parallel execution for:
- Sequential scans
- Index-only scans
- Joins (hash, merge, nested loop)
- Aggregations
Key parameters:
- max_parallel_workers
- max_parallel_workers_per_gather
- parallel_setup_cost
- parallel_tuple_cost
π Parallelism can significantly improve performance for analytical workloads.
Step 4: Load Data Efficiently
For bulk data loads:
- Use COPY instead of multiple INSERTs.
- Wrap inserts in a transaction (
BEGIN β¦ COMMIT
). - Temporarily disable WAL logging with
UNLOGGED
tables or setwal_level = minimal
during load. - Drop and recreate indexes after loading large tables.
Step 5: Consider Non-Durable Settings (with Caution)
For test environments or cases where durability is less critical:
- fsync = off β Faster writes, but risk of corruption.
- synchronous_commit = off β Skips WAL flush on every commit.
- full_page_writes = off β Reduces WAL size but increases risk during crashes.
β οΈ Use only when data loss is acceptable.
Step 6: Use Monitoring Tools
Leverage Postgres Enterprise Manager (PEM) or open-source tools like pgBadger and pg_stat_statements:
- PEM Tuning Wizard suggests configuration changes.
- Capacity Manager projects storage growth.
- Performance dashboards show memory, CPU, I/O usage in real-time.
Quick Performance Tuning Checklist
- β Optimize hardware (disks, RAM, CPU).
- β Put WAL on separate disks.
- β Adjust memory settings (shared_buffers, work_mem, maintenance_work_mem).
- β Tune WAL and checkpoint parameters.
- β Enable parallel queries.
- β Load data with COPY and bulk strategies.
- β Monitor continuously with PEM or pgBadger.
Final Thoughts
PostgreSQL provides DBAs with the flexibility to fine-tune performance for a wide variety of workloads.
Start with the operating system, move to PostgreSQL parameters, and use monitoring tools to validate improvements.