PostgreSQL Performance Cheat Sheet

EXPLAIN, vacuum, planner settings, indexing, and practical tuning recipes.

View
StandardDetailedCompact
Export
Copy the compact sheet, download it, or print it.
Download
`D` dense toggle · `C` copy all
## EXPLAIN and Plan Analysis
EXPLAIN query
EXPLAIN SELECT * FROM orders WHERE customer_id = 42;

# Show the planner's chosen execution plan.

EXPLAIN ANALYZE
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE customer_id = 42;

# Run the query and show actual timing and buffer usage.

EXPLAIN VERBOSE
EXPLAIN (ANALYZE, VERBOSE, BUFFERS, SETTINGS) SELECT * FROM orders WHERE customer_id = 42;

# Include more detail about output columns and active settings.

EXPLAIN JSON format
EXPLAIN (ANALYZE, FORMAT JSON) SELECT * FROM orders;

# Return the plan as machine-readable JSON.

Enable auto_explain
LOAD 'auto_explain';
SET auto_explain.log_min_duration = '200ms';
SET auto_explain.log_analyze = on;

# Log slow query plans automatically in the server logs.

## Vacuum, Analyze, and Maintenance
Vacuum one table
VACUUM public.users;

# Reclaim dead tuples and maintain visibility data.

Vacuum analyze one table
VACUUM (ANALYZE) public.users;

# Vacuum and refresh planner stats in one command.

VACUUM FULL table
VACUUM (FULL, ANALYZE) public.users;

# Rewrite a bloated table more aggressively.

Reindex table
REINDEX TABLE public.users;

# Rebuild all indexes on a table.

Analyze verbose
ANALYZE VERBOSE public.users;

# Show extra detail while updating planner statistics.

Show autovacuum setting
SHOW autovacuum;

# Check whether autovacuum is enabled.

## Planner Settings and Statistics
Show work_mem
SHOW work_mem;

# Inspect a setting that affects sort and hash memory usage.

Set LOCAL work_mem
SET LOCAL work_mem = '256MB';

# Increase work_mem for the current transaction only.

Show effective_cache_size
SHOW effective_cache_size;

# Inspect a planner estimate of OS cache availability.

Show random_page_cost
SHOW random_page_cost;

# Inspect a cost parameter that influences index-vs-seqscan decisions.

Disable seqscan for investigation
SET enable_seqscan = off;

# Temporarily discourage sequential scans while investigating plans.

Inspect pg_stats
SELECT schemaname, tablename, attname, n_distinct, most_common_vals
FROM pg_stats
WHERE tablename = 'orders';

# Inspect statistics the planner uses.

## High-value Query Performance Patterns
Keyset pagination
SELECT *
FROM orders
WHERE (created_at, order_id) < ('2026-03-01', 1000)
ORDER BY created_at DESC, order_id DESC
LIMIT 50;

# Prefer keyset pagination over deep OFFSET scans for large result sets.

Select needed columns
SELECT order_id, customer_id, total_amount FROM orders WHERE status = 'open';

# Avoid fetching unnecessary columns, especially wide rows.

Covering index with INCLUDE
CREATE INDEX idx_orders_customer_created_inc
ON orders (customer_id, created_at DESC)
INCLUDE (status, total_amount);

# Use included columns to support index-only reads for certain queries.

Batch updates
UPDATE orders
SET archived_at = now()
WHERE order_id IN (
  SELECT order_id FROM orders WHERE created_at < now() - interval '1 year' LIMIT 10000
);

# Chunk large writes into batches to reduce contention and WAL spikes.

Batch deletes
DELETE FROM audit_log
WHERE ctid IN (
  SELECT ctid FROM audit_log WHERE created_at < now() - interval '180 days' LIMIT 10000
);

# Delete in chunks to avoid huge transactions.

Create range-partitioned table
CREATE TABLE metrics (
  ts timestamptz NOT NULL,
  device_id bigint NOT NULL,
  value numeric NOT NULL
) PARTITION BY RANGE (ts);

# Partition large time-series tables by range.

Attach partition
CREATE TABLE metrics_2026_03 PARTITION OF metrics
FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');

# Add a partition for a date range.

Recommended next

No recommendations yet.