SynfraCore
Synfracore
Start Learning
Navigation

Academies

Platform

RoadmapsLabsCertificationsInterviewPYQsAI AssistantCareer
Start Learning Free🗺️ Learning Roadmaps

Performance TuningCheatsheets

Quick reference — commands, syntax, and patterns

✍️
Written by senior engineers. Reviewed for technical accuracy.· Updated 2025 · SynfraCore Performance Tuning Team
Expert Content

Database Performance Tuning — Quick Reference

Query plan reading — key terms

TermMeaning

|---|---|

Seq ScanFull table scan — usually means a missing/unusable index
Index ScanUses an index, then fetches the row from the table
Index Only ScanSatisfies the query entirely from the index — no table fetch needed
Bitmap Heap ScanGood for range queries matching a moderate row count
Nested LoopFine if the inner side is small; bad if it's large
Hash JoinGenerally fine for large datasets
Rows Removed by FilterHigh value = the index/scan found far more rows than needed — a red flag

Finding slow queries

sql
-- PostgreSQL — top queries by average execution time
SELECT query, calls, round(total_exec_time::numeric / calls, 2) AS avg_ms
FROM pg_stat_statements ORDER BY avg_ms DESC LIMIT 20;

-- MySQL — enable slow query log
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;

Index diagnostics

sql
-- Tables with heavy sequential scanning (candidates for a new index)
SELECT relname, seq_scan, seq_tup_read
FROM pg_stat_user_tables WHERE seq_scan > 100 ORDER BY seq_tup_read DESC;

-- Unused indexes (wasted write cost, candidates for removal)
SELECT indexrelname, idx_scan, pg_size_pretty(pg_relation_size(indexrelid))
FROM pg_stat_user_indexes WHERE idx_scan < 10 ORDER BY pg_relation_size(indexrelid) DESC;

-- Create an index without locking writes
CREATE INDEX CONCURRENTLY idx_name ON table_name(column_name);

-- Refresh planner statistics after a large data change
ANALYZE table_name;

Bloat / autovacuum diagnostics

sql
-- Dead tuple ratio per table
SELECT relname, n_live_tup, n_dead_tup,
       round(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 1) AS dead_pct
FROM pg_stat_user_tables ORDER BY dead_pct DESC;

Lock/deadlock diagnostics

sql
-- Currently blocked queries and what's blocking them
SELECT blocked_locks.pid AS blocked_pid, blocking_locks.pid AS blocking_pid,
       blocked_activity.query AS blocked_query
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype
    AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;

Query rewrite patterns

sql
-- Correlated subquery (slow) → JOIN + GROUP BY (fast)
-- IN (subquery) (can be slow) → EXISTS (stops at first match)
-- LIKE '%term%' (can't use index) → full-text search (to_tsvector/to_tsquery)

Key postgresql.conf settings

ini
shared_buffers = 25%           # of total RAM
effective_cache_size = 75%     # of total RAM
work_mem = 64MB                # per sort/hash op, per connection
maintenance_work_mem = 512MB   # for VACUUM, CREATE INDEX
random_page_cost = 1.1         # for SSD (default 4.0 assumes spinning disk)
max_connections = 200          # use PgBouncer beyond this
autovacuum_vacuum_scale_factor = 0.05   # lower for high-write tables (default 0.2)

The tuning hierarchy

1. Schema design       (hardest to change later)
2. Query optimization  (EXPLAIN ANALYZE first, always)
3. Index strategy      (usually the highest-impact quick win)
4. Configuration       (memory, connections, autovacuum)
5. Hardware/infra      (last resort, not the first instinct)
Share:
Join our Community
Daily tips, job alerts, interview help — join engineers learning together
Also Worth Exploring
← Back to all Performance Tuning modules
CertificationNotes