Contents
In brief
When an app feels slow, the database is usually the bottleneck — not the framework or CDN. A Dev.to guide from a nine-year backend veteran lists six strategies that cut query time by up to 60% on real projects.
What happened
The author’s rule: never optimize a query you have not profiled. Step one — slow query log (~200 ms threshold in PostgreSQL and MySQL) and a weekly review. Step two — EXPLAIN ANALYZE for real timings, not paper plans.
Production example: a vacancy search did a sequential scan over 200k rows, filtered 187k, and took 1.8 s. A composite index on (status, created_at DESC) dropped that to 12 ms — a 99.3% win. Next: partial indexes (only status = 'active', ~85% smaller), covering indexes with INCLUDE for index-only scans without heap access.
ORM pitfalls get their own section: N+1 (150 queries per page → 8 after eager loading), correlated subqueries vs JOINs, analytics with window functions instead of five app round-trips. For read-heavy dashboards — materialized views (cron refresh), strategic denormalization (JSONB stats on an organisation row).
Infrastructure: PgBouncer (500 client connections on 25 backend), read replicas for search and reporting, Redis with TTL and event-driven invalidation (vacancy:created).
Why it matters
Full-stack teams often tune React while p95 API latency comes from one heavy SELECT. Random indexes can slow writes; without EXPLAIN you will not see the plan change. Materialized views and cache are not “anti-patterns” when staleness is measured in minutes, not milliseconds.
In practice
- Enable slow query logging; weekly, take the top 20 slowest queries.
- Run EXPLAIN ANALYZE on each; hunt seq scans on tables >10k rows and sorts without index support.
- Composite indexes: column order must match filter and sort order in the query.
- Audit ORM output: N+1,
withCount, replace subqueries with JOINs/CTEs. - Read-heavy dashboards — materialized view + periodic
REFRESH CONCURRENTLY. - Pooling (PgBouncer) and replicas before caching what SQL could fix.
Takeaway
Database optimization is ongoing engineering, not a one-off sprint. The six layers in the article (profile → indexes → SQL → schema → connections → cache) are a solid checklist before the next “make the frontend faster” ticket.