Caching and Performance Engineering · Performance and Scalability

Database Performance

Indexes, query plans, N+1 queries and connection pooling.

Most backend latency is database time. Read query plans (EXPLAIN ANALYZE), add indexes that match filters and sort order (composite, covering, partial), eliminate N+1 patterns with joins or batched IN queries, fetch only needed columns, use keyset pagination, keep transactions short, and size connection pools (plus PgBouncer/ProxySQL) to what the database can handle. Move heavy analytics to replicas or warehouses.

EXPLAIN (ANALYZE, BUFFERS)
SELECT id, total_cents FROM orders
WHERE customer_id = 42 AND status = 'paid'
ORDER BY created_at DESC LIMIT 20;
-- Seq Scan on orders (actual time=0.03..812.4 rows=20) Filter rows removed: 2,400,000   ← problem

CREATE INDEX CONCURRENTLY idx_orders_customer_status_created
  ON orders (customer_id, status, created_at DESC) INCLUDE (total_cents);
-- Index Only Scan ... (actual time=0.05..0.21 rows=20)                                  ← fixed

Common pitfalls

  • Indexes on every column (slower writes, unused indexes).
  • ORM lazy loading in loops.
  • More app instances × pool size than the database allows.

Key points

  • EXPLAIN ANALYZE before and after
  • Composite/covering indexes match queries
  • Fix N+1, bound pools

Further reading: PostgreSQL: Using EXPLAIN

Practice questions

Why does column order matter in a composite index?

Answer

B-tree indexes are sorted by the first column, then the next; queries can use the index efficiently only for leftmost-prefix filters (and matching sort order).

How can adding app servers make database performance worse?

Answer

Each instance opens its own connection pool; too many connections exhaust database memory and cause contention. Use poolers and bound total connections.

Open in the app to mark lessons complete, take notes and track your progress.