Module 16 · Query Optimization · Reference Library Home · Troubleshooting · Casebook · Lessons
Disclaimer: These scenarios are fictionalized engineering case studies based on common production failure modes observed across the industry. Company names, metrics, architectures, and business impacts are illustrative. No attribution to any real company is intended unless an external source is explicitly cited.
Realistic post-mortems from high-traffic systems. Each follows SRE structure: Architecture → Symptoms → Diagnosis → Root Cause → Fix → Business Impact → Lessons Learned.
Map each incident to the relevant lesson before reading the fix.
flowchart LR
I1[Recommendation Feed<br/>Partial Index] --> L03[Lesson 03]
I2[Payment Reconciliation<br/>NOT IN NULL] --> L05[Lesson 05]
I3[Driver Earnings<br/>Correlated Subquery] --> L05
I4[Search Pagination<br/>OFFSET] --> L06[Lesson 06]
I5[Analytics Report<br/>Stale Statistics] --> L01[Lesson 01]
I6[Listing Search<br/>OR Across Columns] --> L06
I7[Peak Traffic<br/>Index Bloat] --> L03
I8[Inventory Report<br/>Join Explosion] --> L04
watch_events table with billions of rows (partitioned by month)Nightly batch job recomputes per-user viewing scores. After a schema migration adding content_rating column, the job exceeded its 4-hour SLA window.
pg_stat_activity: parallel workers all in Seq Scan on watch_eventsHashAggregate (actual time=9840000..9840000 rows=220000000)
-> Seq Scan on watch_events (actual time=0.05..8200000 rows=4000000000)
Filter: (watch_date >= '2026-01-01' AND content_rating IS NOT NULL)
Rows Removed by Filter: 36000000000
Migration added content_rating (nullable, 60% NULL). Existing partial index on watch_date couldn’t serve the new IS NOT NULL filter. Optimizer chose Seq Scan across all partitions.
Non-SARGable combined filter: content_rating IS NOT NULL on a column without a partial index, combined with a date range that crossed partition boundaries without pruning.
CREATE INDEX CONCURRENTLY idx_watch_events_date_rating
ON watch_events (watch_date, content_rating)
WHERE content_rating IS NOT NULL;
-- Rewrite to push filter into each partition explicitly
SELECT user_id, COUNT(*) AS views
FROM watch_events
WHERE watch_date >= '2026-01-01'
AND content_rating IS NOT NULL
GROUP BY user_id;
EXPLAIN ANALYZE on batch queries after schema migrations (note: EXPLAIN ANALYZE executes the query — use with caution on production data)Marketing automation campaign targeting “inactive payers” sent zero emails for 3 days before anyone noticed.
Not applicable — query was logically wrong, not slow.
-- The production query
SELECT account_id FROM accounts
WHERE account_id NOT IN (
SELECT account_id FROM payments WHERE status = 'succeeded'
);
-- payments.account_id is NULL for ~12,000 orphaned payment records
NOT IN with NULL in subquery result set. Three-valued logic: x NOT IN (a, b, NULL) evaluates to UNKNOWN for every row, not TRUE. Query silently returns zero rows.
SELECT a.account_id FROM accounts a
WHERE NOT EXISTS (
SELECT 1 FROM payments p
WHERE p.account_id = a.account_id
AND p.status = 'succeeded'
);
NOT IN against nullable columnsNOT IN subqueries in production SQLMonday morning driver earnings dashboard (peak login window) caused a prolonged outage.
Too many connections-> Nested loop inner join (actual time=28000..28000 rows=5000000 loops=1)
-> Index scan on trips using idx_driver_date (rows=5000000)
-> Dependent subquery (actual time=0.005..0.005 rows=1 loops=5000000)
-> Aggregate: avg(commission_rate) (loops=5000000)
-> Index scan on commission_tiers using idx_tier (loops=5000000)
Correlated scalar subquery in SELECT list executed millions of times — once per trip row.
ORM-generated view with projected correlated subquery: (SELECT AVG(commission_rate) FROM commission_tiers WHERE tier = t.tier_id). SubPlan node with extremely high loop count.
-- Pre-aggregate commission rates, then join (avoids fan-out)
WITH commission_avg AS (
SELECT tier_id, AVG(commission_rate) AS avg_commission
FROM commission_tiers
GROUP BY tier_id
)
SELECT t.driver_id, t.trip_id, t.fare, ca.avg_commission
FROM trips t
JOIN commission_avg ca ON ca.tier_id = t.tier_id
WHERE t.trip_date >= CURRENT_DATE - INTERVAL 7 DAY;
Latency improvement was substantial in the production environment; measure the actual impact for your dataset and schema.
Admin panel page 500+ took 30+ seconds; page 1 took 200ms. Users reported “the admin panel is broken.”
EXPLAIN: Using filesort + Using temporary on every paginated query-> Sort: repos.created_at (actual time=28000..28000 rows=10000)
-> Index scan on repos using idx_org (rows=50000)
Filter: (org_id = 12345)
Limit: 20 OFFSET 10000
OFFSET-based pagination on a large result set. Engine generates and discards thousands of rows before returning the page.
LIMIT 20 OFFSET 10000 — cost grows linearly with offset. No keyset pagination implemented.
-- Keyset pagination (flat cost regardless of page depth)
SELECT repo_id, repo_name, created_at
FROM repos
WHERE org_id = 12345
AND created_at < '2026-01-15T10:30:00' -- cursor from previous page
ORDER BY created_at DESC
LIMIT 20;
Deep-page latency improved dramatically; measure the actual difference in your environment.
Daily analytics report runtime jumped significantly after a bulk data import, with no SQL changes.
EXPLAIN: estimated rows vastly different from actual rows on filtered nodeNested Loop (actual time=14400000..14400000 rows=3 loops=1)
-> Seq Scan on request_logs (estimated rows=400000, actual rows=3)
Filter: (zone_id = 99999)
-> Index Scan on zones (rows=1)
Bulk import of billions of rows without running ANALYZE. Optimizer statistics still reflected pre-import distribution. Estimated 400K rows for a zone that actually had 3 rows post-filter.
Stale statistics after bulk load. Optimizer chose Nested Loop expecting hundreds of thousands of outer rows; actual was 3. The plan shape wasn’t wrong for the estimated data — it was wrong for the actual data.
-- PostgreSQL
ANALYZE request_logs;
-- Re-run query: optimizer now chooses Hash Join with correct estimates
-- MySQL equivalent:
-- ANALYZE TABLE request_logs;
Also added post-import ANALYZE to the ingestion pipeline.
ANALYZE after bulk loads — automate it in the ingestion pipelineSearch query for popular cities took several seconds; engineering assumed missing index.
(city, price) existed but query still slowEXPLAIN: two scans merged with BitmapOr(neighborhood, price) also existed but neither used efficientlyBitmap Heap Scan on listings (actual time=7800..7800 rows=45000)
Recheck Cond: ((city = 'Paris') OR (neighborhood = 'Le Marais'))
-> BitmapOr
-> Bitmap Index Scan on idx_city_price
-> Bitmap Index Scan on idx_neighborhood_price
OR across two different indexed columns forces BitmapOr — reads both indexes, merges bitmaps, then rechecks heap. Slower than two independent index scans.
OR across unrelated columns — neither single index efficiently serves the combined predicate.
SELECT listing_id, title, price FROM listings
WHERE city = 'Paris' AND price BETWEEN 50 AND 200
UNION ALL
SELECT listing_id, title, price FROM listings
WHERE neighborhood = 'Le Marais' AND price BETWEEN 50 AND 200
AND city <> 'Paris';
Note: The
AND city <> 'Paris'exclusion in the second branch prevents duplicate rows in theUNION ALLresult. Ifcityis nullable, also addOR city IS NULL.
Latency improvement was substantial; measure the actual difference in your environment.
shop_id + order_status during peak trafficOrder lookup queries that normally ran in single-digit milliseconds spiked during a peak sales event.
(shop_id, order_status) existedSHOW INDEX FROM orders: index pages significantly larger than normal-> Index range scan on idx_shop_status (actual time=5..2000 rows=50000)
Filter: (order_status = 'pending')
Rows examined: 5000000 (should be 50000)
High-volume INSERT/UPDATE during peak traffic caused index fragmentation. Index scan examined far more rows than necessary due to fragmented index pages.
Over-indexing (dozens of indexes on orders table) combined with write-heavy peak traffic. Index maintenance overhead couldn’t keep pace with insert rate.
OPTIMIZE TABLE orders during low-traffic windowsys.schema_unused_indexes)Weekly inventory report exceeded its batch window after adding 2 new warehouse tables to the query.
Planning time dominated total runtime. Execution time was also very high.
10-table join with no early filters. Optimizer couldn’t find optimal join order within time budget.
Join order explosion — too many tables joined without predicate pushdown to reduce intermediate result sizes early.
LEADING hint for critical join order on largest tables (Oracle-specific)Runtime improved dramatically; measure the actual improvement for your environment.
| Incident | Smell | Lesson | Fix Pattern |
|---|---|---|---|
| Recommendation Feed Timeout | Non-SARGable + missing partial index | 03 | Add partial index |
| Payment Reconciliation NULL Trap | NOT IN NULL trap | 05 | NOT EXISTS |
| Driver Earnings CPU Spike | Correlated subquery in SELECT | 05 | Pre-aggregate + JOIN |
| Search Pagination Degradation | Large OFFSET pagination | 06 | Keyset pagination |
| Stale Statistics Regression | Stale statistics | 01, 02 | ANALYZE |
| Listing Search OR Regression | OR across columns | 06 | UNION ALL |
| Index Maintenance Incident | Over-indexing / bloat | 03 | Remove unused indexes |
| Join Explosion Incident | Join order explosion | 04 | Staged CTEs + hints |