Core Concept

Indexing and Query Optimization

Indexes turn full table scans into tree lookups; match index columns to your query patterns before reaching for sharding or more hardware.


1. What It Is

Before you add Redis or shard the database, ask whether the right index would fix the slow query. Indexing is how you turn full table scans into millisecond lookups β€” and how you accidentally slow down every write.

What:

An index is a secondary database access path built on top of primary tables.

Primary purpose:

Accelerating read query speeds by replacing slow full table disk scans with binary tree traversals.

Usually used for:

Filtering rows (WHERE), sorting datasets (ORDER BY), joining tables, and resolving point lookups.

2. Core Mental Model

An index is a sorted lookup structure β€” design columns to match the WHERE and ORDER BY clauses you actually run:

πŸ” Secondary Access Path

Instead of reading millions of database rows sequentially from disk, follow a tree branch directly to the row pointer in O(log N) steps.

βš–οΈ Write & Memory Tax

Indexes are not free. Every added index slows database mutations because the engine must update both the raw table and the index tree.

⛓️ Left-to-Right Sort Order

Composite (multi-column) indexes are sorted hierarchically. They only support queries matching from left to right (leftmost prefix rule).

In the room

Candidates propose caching before checking indexes. Walk through the query plan: "This lookup is O(n) without an index on user_id; a B-tree index makes it O(log n)." Mention composite index column order if the query filters on multiple fields.

3. Why It Matters in HLD

Indexes are how we turn full table scans into point lookups β€” but every index taxes writes. We frame the discussion around three lenses:

Needed When:

Query latencies spike, reads heavily outnumber writes, or database queries scan more than a small fraction of rows.

Avoids:

Full table scans, database disk I/O exhaustion, high query response variance, and CPU starvation.

Optimizes For:

Time to First Byte (TTFB), index-only reads (covering index), and stable response times under heavy concurrent loads.

4. Architecture & Data Flow

Walk the query path as interview steps. Step 1 β€” Parse: SQL arrives at the optimizer. Step 2 β€” Plan: optimizer chooses index seek vs sequential scan based on selectivity and stats. Step 3 β€” Index seek: B-tree traversal to matching rows β€” O(log n) instead of O(n). Step 4 β€” Covering index: if all SELECT columns live in the index, skip the heap fetch. Step 5 β€” Write path: every INSERT/UPDATE maintains index pages β€” name the write amplification.

Loading...

5. Key Characteristics

These index types and optimizer behaviors are what we cite when sizing read vs write cost:

  • Clustered Index: Organizes the raw table rows on disk physically sorted by the primary key (maximum one per table).
  • Non-Clustered Index: A separate tree storing secondary keys pointing to primary key addresses.
  • Partial Index: Indexes only rows matching a predicate (e.g. WHERE status = 'ACTIVE') β€” smaller and faster for filtered hot paths.
  • Covering Index: Includes all columns in the SELECT clause so the engine never touches the heap β€” enables index-only scans.
  • Leftmost Prefix Rule: A composite index on (A, B, C) only helps queries that filter on column A first.
  • Index type matrix β€” match structure to query pattern:
TypeLookupRangeUse Case
B+Tree (General purpose)O(log N)Excellent (linked leaves)Standard SQL/NoSQL primary/secondary indexes
Hash (Point lookup)O(1)None (unordered keys)Redis, exact match filters in SQL databases
Covering (Index-only)O(log N)ExcellentQueries reading ONLY the indexed columns (zero disk row seek)
Inverted (Full-text)Token-basedN/ASearch engines like Elasticsearch, PostgreSQL GIN/GiST
Geospatial (R-Tree / S2)O(log N)ProximityLocation queries near coords (PostGIS, Uber, MongoDB)

In the room

When you propose an index, say what query it serves and what write cost you accept. "Add an index on user_id" without naming the SELECT is half an answer.

6. Strategic Tradeoffs

Indexes accelerate reads but slow writes β€” we articulate the trade-off:

BenefitCost
Drastically Faster Reads (swaps slow full table scans for O(log N) lookups)Slower Writes (every INSERT/UPDATE/DELETE must update all secondary indexes)
Efficient Range Queries & ORDER BY (linked leaf nodes keep data physically sorted)Memory & Disk Overhead (large indexes consume RAM buffer space and storage)
Covering Index Optimization (serves select queries entirely from index metadata)Maintenance Overhead (indexes require periodic defragmentation/rebuilding)

7. Failure / Bottleneck Awareness

Missing indexes and over-indexing both kill production systems β€” we name the failure modes:

🚫 Leftmost Prefix Rule Violation

Problem: A developer builds a composite index on (first_name, last_name) but queries with WHERE last_name = 'Smith'. The database optimizer ignores the index and falls back to a slow full table scan.

Mitigation: Ensure query patterns align from left to right with indexed columns, or add separate indexes tailored to individual filters.

🐒 OFFSET Pagination Bottleneck

Problem: Standard pagination query LIMIT 20 OFFSET 50000 gets progressively slower. The database must traverse and discard 50,000 index entries to read the target 20.

Mitigation: Switch to cursor-based pagination (keyset pagination): WHERE id > :last_seen_id ORDER BY id LIMIT 20 to jump directly to the starting offset.

❌ Wrapping Columns in Functions

Problem: Querying WHERE LOWER(email) = 'user@example.com' on an indexed email column bypasses the index entirely because database runtime calculations modify keys during search.

Mitigation: Query with exact casing matching the index, or construct a specialized functional index: CREATE INDEX ... ON users (LOWER(email)).

8. Common HLD Usage

These patterns show where indexing changes the architecture conversation:

ProblemUsage
WhatsApp Message TimelineComposite index on (chat_thread_id, timestamp) for rapid chat histories
Uber Driver Dispatch ProximityGeospatial index (S2 / Geohash cells) to find nearby drivers in milliseconds
E-commerce Faceted SearchInverted index (Elasticsearch) to filter product catalogs by color, size, price
Instagram Feed Timeline GenerationIndex on (user_id, created_at DESC) for cursor-based user home timelines
API Idempotency Key ValidationUnique B+Tree or Hash index on (idempotency_key) to intercept duplicate requests

9. Decision Signals

Reach for index discussion when point lookups or sort-heavy queries dominate the hot path:

🎯 Think Database Indexing When:
  • Slow queries show high rows_examined in comparison to rows_sent.
  • Your database reads far outnumber mutations (e.g. system read-heavy metadata lookup).
  • A high volume of queries perform filters, aggregations, or strict sorting.
  • You face relational joins (foreign keys must always be indexed to prevent slow join scans).
  • You must guarantee domain integrity (e.g. enforcing email uniqueness).

11. Deep Dive (Optional)

In interviews, propose indexes on columns that appear in frequent WHERE, JOIN, and ORDER BY clauses β€” email for login, user_id on child tables, composite keys for timeline queries. For search beyond SQL, mention Elasticsearch or PostGIS synced via CDC with acceptable lag.

Clustered vs Non-Clustered Storage Mechanics

In clustered indexes (e.g., MySQL's InnoDB primary key), leaf nodes store the actual physical data row on disk. In secondary non-clustered indexes, the leaf nodes store the secondary search key and the primary key as a row pointer. To retrieve non-indexed columns, the database must traverse the secondary index tree, find the primary key, and then traverse the primary clustered index tree (double lookup or key lookup).

Composite Index Rule Mathematics

When planning multi-column composite indexes, you must adhere to the Equality-first, Sort-next, Range-last optimization math:

SQL
-- For Query: 
WHERE status = 'ACTIVE' AND user_id = 100 AND created_at > '2024-01-01' 
ORDER BY created_at DESC;

-- Best Composite Index columns:
(user_id, status, created_at)
  • Columns filtered with exact match (=) must come first.
  • Columns utilized in sorting (ORDER BY) come second.
  • Columns queried with range comparisons (>, <, BETWEEN) must come last. Range columns break B+Tree index traversal chain for subsequent columns.

Covering Indexes & Index-Only Scans

A covering index contains all columns requested in the query. For example, if you index (user_id, status) and query: SELECT status FROM users WHERE user_id = 10, the database reads the index leaf node directly and returns the response. It completely skips accessing raw table data on disk, reducing I/O operations to near zero.

Query Execution Analysis (EXPLAIN)

To verify database optimizer decisions, prefix queries with EXPLAIN or EXPLAIN ANALYZE. High-performing queries will list:

  • type: ref or eq_ref (using index lookup).
  • Extra: Using index (confirming covering index).
  • Low rows scanned ratio (ideally close to returned count).
  • Avoid Extra: Using filesort or Extra: Using temporary (indicates slow non-indexed in-memory sorting).

LSM-Tree Write Path (Cassandra, RocksDB, LevelDB)

B+Tree indexes optimize for read latency β€” every INSERT updates random pages on disk, which hurts write-heavy workloads. Log-Structured Merge (LSM) trees flip the model: writes append sequentially to an in-memory memtable and a write-ahead log; when the memtable fills, it flushes to immutable SST files on disk. Reads check memtable β†’ bloom filter β†’ SST layers (newest to oldest).

  • Write amplification: Background compaction merges SST files β€” disk I/O continues after writes "complete."
  • Read amplification: A key may exist in multiple SST layers until compaction runs; bloom filters skip absent keys cheaply.
  • Interview fit: Choose LSM-backed stores for append-heavy telemetry, messaging metadata, and write-heavy KV β€” choose B+Tree OLTP when complex indexed reads dominate.

πŸ’¬Review

Help Us Improve

How helpful was this walkthrough?

Click a star to rate. We actively use this feedback to refine and update our system design content.

Placeholder
Optional but highly appreciated!

Discussion

Share your thoughts, ask questions, or help others.

Loading comments...