Clustered vs Non-Clustered Indexes

⭐ Interview Importance: HIGH
⏱️ Revision Time: 4 min

Concept

When you create an index, you are building a B-Tree. But what exactly is sitting inside the “Leaf Nodes” at the very bottom of the tree? Is it a pointer to the data, or is it the data itself?
This distinction separates Clustered Indexes from Non-Clustered Indexes.

Understanding this is the absolute key to database optimization and understanding why MySQL and PostgreSQL behave entirely differently.

1. Clustered Index

A Clustered Index dictates the physical sorting order of the table on the hard drive.
Because data can only be physically sorted one way on a disk, a table can only have exactly one Clustered Index (usually the Primary Key).

In a Clustered Index, the Leaf Nodes of the B-Tree ARE the data. The entire row (name, email, age, password) is physically stored inside the bottom of the B-Tree.

Analogy: A dictionary. The words are sorted alphabetically. You flip to the word “Apple” (the index), and the definition (the data) is printed right next to it. You don’t have to go look somewhere else.

Performance: SELECT * FROM users WHERE id = 10. Extremely fast. The database traverses the B-Tree to find ID 10, and the entire user record is sitting right there.

2. Non-Clustered Index (Secondary Index)

A Non-Clustered Index does not alter the physical storage of the table. It is a completely separate structure created on the side.
A table can have multiple Non-Clustered Indexes (e.g., an index on email, an index on last_name).

In a Non-Clustered Index, the Leaf Nodes contain the indexed value (e.g., the email) and a Pointer.

Analogy: The index at the back of a textbook. You look up “Photosynthesis” (the index). It says “Page 42” (the pointer). You must then flip to Page 42 to actually read the data.

Performance: SELECT * FROM users WHERE email = 'alice@mail.com'.

  1. The database traverses the Non-Clustered email B-Tree to find ‘alice’.
  2. It finds the pointer.
  3. It performs a Bookmark Lookup (or Heap Fetch), jumping across the hard drive to find the actual row data.
    This is slightly slower because it requires two separate disk jumps.

The MySQL vs PostgreSQL Divide

The two most popular open-source databases handle this completely differently.

MySQL (InnoDB)

  • Uses Index-Organized Tables.
  • The Primary Key is automatically the Clustered Index. All row data lives inside the Primary Key B-Tree.
  • Non-Clustered Indexes do not store physical disk pointers. Instead, they store the Primary Key value.
  • The Penalty: If you query by email in MySQL, it searches the email index, finds Primary Key 10, then it must traverse the entire Primary Key B-Tree from scratch to find the data.

PostgreSQL

  • Uses Heap-Organized Tables.
  • There are NO Clustered Indexes in PostgreSQL. The physical rows are just dumped randomly into an unsorted file called a Heap.
  • ALL indexes (even the Primary Key) are Non-Clustered. They all store direct physical disk pointers (CTIDs) that point directly to the Heap.
  • (Note: PostgreSQL has a CLUSTER command, but it is a one-time operation that physically sorts the data and immediately degrades; it is not a continuously maintained Clustered Index).

Interview Questions

Q: A table in MySQL has a massive VARCHAR(255) column as its Primary Key. You add 5 secondary Non-Clustered Indexes to other columns to speed up searches. Why is your hard drive suddenly out of space?
A: In MySQL (InnoDB), every single Non-Clustered Secondary Index stores a copy of the Primary Key value inside its leaf nodes (acting as the pointer). If your Primary Key is a massive 255-byte string, every single entry in all 5 secondary indexes now contains that massive 255-byte string. This causes catastrophic index bloat. This is why Primary Keys in MySQL must always be the smallest possible data type (like a 4-byte INT).

Q: What is a “Bookmark Lookup” (or Key Lookup) and why do DBAs try to avoid it?
A: A Bookmark Lookup occurs when you use a Non-Clustered Index to find a row, but the SELECT statement asks for columns that aren’t inside the index. The database must follow the pointer back to the main table (the Clustered Index or the Heap) to retrieve the missing columns. This extra physical jump to the hard drive is extremely slow. If you return 10,000 rows, that is 10,000 random disk jumps. To fix this, DBAs create a Covering Index, which includes the extra columns directly inside the index structure, eliminating the need for the lookup entirely.