top of page

Database Indexes

  • Writer: Pradeep P
    Pradeep P
  • 4 days ago
  • 4 min read

Layer 1 · Post 13 of 15

← Previous: SQL vs NoSQL → Next: Replication

Layer 1 — The building blocks · Post 13 of 88

An index is a shortcut the database keeps so it does not have to scan every row. The tradeoff is extra storage and slower writes.

What you'll learn

  • What a full table scan is, and why it explodes with growth

  • How a B-tree index lets you jump to a row

  • Why "index everything" makes writes and storage worse

The idea in one minute

Without an index, finding email = 'a@b.com' means reading every row (a sequential scan). That is fine for 200 users. It is not fine for 200 million.

An index is extra data the database maintains — often a B-tree — that is sorted by the column (or columns) you care about. Lookup becomes "walk a short tree," not "read the pile."

You pay for that shortcut on every write: inserts, updates, and deletes must update the indexes too.

Why it matters

"The database is slow" is often "we forgot an index" or "we have the wrong index."

Indexes are also how you encode access patterns in SQL land. If you always load orders by user_id and created_at, that pair wants an index. If you never filter on favorite_color, indexing it is dead weight.

In interviews, saying "we'll add indexes on the lookup keys" is the difference between a toy table and a design that survives a year of growth.

How it works

Table scan vs index lookup

Imagine a phone book.

  • Scan: start at A, read every name until you find yours. Cost grows with the book.

  • Index: the book is already sorted by name. You jump near the right page.

A typical B-tree index keeps keys in sorted order in a disk-friendly tree. The database follows a few pages down to the matching key, then uses a pointer to the row (or finds the row in a covering index).

Hash indexes (less common as a default) are great for exact equality, not for WHERE created_at > ....

Primary keys

The primary key is an index (and more): it uniquely identifies a row. InnoDB even stores the table in primary-key order (clustered index). Random UUIDs as PKs can fragment that layout; sequential or time-ordered ids are often kinder. This is a real-world detail, not trivia.

Composite indexes

Index on (user_id, created_at) helps:

  • WHERE user_id = 9

  • WHERE user_id = 9 ORDER BY created_at

  • WHERE user_id = 9 AND created_at > '2026-01-01'

It does not help much with WHERE created_at > '2026-01-01' alone. Leftmost prefix matters. Order the columns the way you filter.

Covering indexes

If the index contains every column the query needs, the database might not visit the table at all. Fast. Bigger index.

Selectivity

An index on is_deleted when 99% of rows are false is often useless: you still look at almost everyone. Indexes want selective predicates — values that cut the pile down.

A simple example

orders has 50 million rows.

GET /users/42/orders runs WHERE user_id = 42 ORDER BY created_at DESC LIMIT 20.

Without an index, Postgres might scan a huge chunk of the table. With INDEX (user_id, created_at DESC), it walks straight to user 42's newest orders.

Someone adds WHERE status = 'shipped' without extending the index. If most of user 42's orders are shipped, still fine. If not, the planner might still use (user_id, created_at) and filter. If you filter by status without user_id, you need a different index — or you accept a scan.

Common mistakes

Indexing every column "just in case." Writes slow down, backups grow, the planner gets confused, and nothing gets faster.

Functions on the column: WHERE LOWER(email) = ... may not use an index on email unless you have an index on LOWER(email).

Leading wildcards: LIKE '%smith' cannot use a normal B-tree like a prefix search can. Search engines exist for a reason.

Checking production with SELECT * and no LIMIT. You will not notice index issues until the table is large. Test with realistic volume.

Duplicate overlapping indexes. (a) and (a,b) — the second can serve many of the first's queries. Measure before dropping.

How this shows up in real systems

  • EXPLAIN ANALYZE: the tool. "Seq Scan" on a huge table is a smell for that query.

  • Migrations: adding an index on a huge table can lock or slow writes; online/create-concurrently patterns exist for a reason.

  • NoSQL: you still index — partition key, sort key, GSI in DynamoDB, TTL indexes in Mongo. Same idea, different names.

When you design a table, list queries first, then indexes that match them. Not the other way around.

Recap

  • Indexes are sorted shortcuts. They turn scans into lookups.

  • They cost write time and disk. Add them for real queries.

  • Column order in a composite index must match how you filter.

One database on one disk still dies. The next two posts are how data survives and how it splits: replication and sharding.

Layer 1 · Post 13 of 15

← Previous: SQL vs NoSQL → Next: Replication

Comments


About Me

DSC_7604.jpg

Hi, I am a software engineer from Bangalore, India. Love spending time on gaming and photography. This website is where I will ocassionally throw what comes to my mind. Hope it is useful or at least entertaining to you. :)

 

  • Instagram
  • Facebook
  • Twitter
  • LinkedIn
  • YouTube
  • 500px

© 2023 by Going Places. Proudly created with Wix.com

bottom of page