Ask anything about this article
Hi! I've read this article.
What would you like to know?
@farhan
A single missing index can turn a 10ms query into a 10-second query. Database indexing is one of the most impactful performance optimizations you can make, yet most developers treat it as an afterthought. Let's fix that.
An index is a data structure that improves the speed of data retrieval operations on a database table. Think of it like the index at the back of a textbook — instead of reading every page to find a topic, you look it up in the index and jump directly to the right page.
Without an index, the database performs a full table scan — reading every single row to find your data. With millions of rows, this is painfully slow.
The most common index type is the B-Tree (Balanced Tree). Here's how it works:
[50]
/ \
[25] [75]
/ \ / \
[10,20] [30,40] [60,70] [80,90]
↓ ↓ ↓ ↓
[rows] [rows] [rows] [rows]
To find a row where id = 70:
This takes O(log n) comparisons. For a table with 1 million rows, that's only ~20 comparisons instead of 1 million.
sql-- Single column index
CREATE INDEX idx_users_email ON users(email);
-- Composite index (order matters!)
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
-- Unique index (enforces uniqueness)
CREATE UNIQUE INDEX idx_users_username ON users(username);
-- Partial index (only index a subset of rows)
CREATE INDEX idx_orders_pending ON orders(status)
WHERE status = 'pending';
✅ Columns used in WHERE clauses
✅ Columns used in JOIN conditions
✅ Columns used in ORDER BY
✅ Foreign key columns
✅ Columns with high cardinality (many unique values)
❌ Small tables (< 1000 rows)
❌ Columns that are rarely queried
❌ Columns with low cardinality (boolean, status)
❌ Tables with heavy write operations
sql-- Index: (user_id, created_at)
-- ✅ Uses the index (left-most prefix)
SELECT * FROM orders WHERE user_id = 123;
SELECT * FROM orders WHERE user_id = 123 AND created_at > '2024-01-01';
-- ❌ Does NOT use the index efficiently
SELECT * FROM orders WHERE created_at > '2024-01-01';
This is called the leftmost prefix rule. The index can be used for queries that match from the left side of the composite key.
Always verify your indexes are being used:
sqlEXPLAIN ANALYZE SELECT * FROM users WHERE email = 'john@example.com';
-- Good: "Index Scan using idx_users_email"
-- Bad: "Seq Scan on users" (full table scan)
ANALYZE after creating indexes for the query plannerREINDEX to handle fragmentation| Query | Without Index | With Index | Improvement |
|---|---|---|---|
| Find by email | 850ms | 2ms | 425x |
| Filter by date range | 1200ms | 15ms | 80x |
| Join on foreign key | 3500ms | 25ms | 140x |
| Count with condition | 2100ms | 8ms | 262x |
Proper indexing is the difference between a fast application and a slow one. Take the time to analyze your queries and add the right indexes.