DEV Community

Young Gao
Young Gao

Posted on

Database Indexes Explained: B-Trees, Composite Keys, and When Indexes Hurt Performance (2026)

Database Indexes Explained: B-Trees, Composite Indexes, and When They Hurt Performance

You added an index. The query got slower. Here is why, and how to use indexes correctly.

How B-Tree Indexes Work

A B-Tree index is a sorted data structure that lets the database find rows without scanning the entire table. Think of it like a phone book: sorted by last name, you can find "Smith" without reading every entry.

SELECT * FROM users WHERE email = $1;

CREATE INDEX idx_users_email ON users(email);
Enter fullscreen mode Exit fullscreen mode

Composite Indexes

Order matters. A composite index on (country, city) helps queries filtering by country alone, but NOT queries filtering by city alone.

-- This index helps all three queries below
CREATE INDEX idx_orders_status_date ON orders(status, created_at);

-- Uses index (leftmost prefix)
SELECT * FROM orders WHERE status = 'pending';

-- Uses full index
SELECT * FROM orders WHERE status = 'pending' AND created_at > NOW() - INTERVAL '1 day';

-- Does NOT use index (skips leftmost column)
SELECT * FROM orders WHERE created_at > NOW() - INTERVAL '1 day';
Enter fullscreen mode Exit fullscreen mode

When Indexes Hurt

  1. Small tables: Full scan is faster than index lookup for < 1000 rows
  2. High write tables: Every INSERT/UPDATE must also update the index
  3. Low selectivity: Indexing a boolean column (true/false) rarely helps
  4. Too many indexes: Each index costs disk space and slows writes

Partial Indexes

Index only the rows you query:

-- Only index active users (90% of queries)
CREATE INDEX idx_active_users ON users(email) WHERE deleted_at IS NULL;

-- Only index recent orders
CREATE INDEX idx_recent_orders ON orders(created_at) WHERE status \!= 'archived';
Enter fullscreen mode Exit fullscreen mode

EXPLAIN ANALYZE

Always verify your index is being used:

EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';
-- Look for: Index Scan using idx_users_email
-- Red flag: Seq Scan (ignoring your index)
Enter fullscreen mode Exit fullscreen mode

Part of my Production Backend Patterns series. Follow for more practical backend engineering.


You Might Also Like

Follow me for more production-ready backend content!


If this helped you, buy me a coffee on Ko-fi!

Top comments (0)