Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 59

Database Indexing for Query Optimization

Recommended resources

Hello! Welcome to the next lesson in our "Database Architecture & Design" module.

In our last lesson, we compared SQL and NoSQL databases from an architectural standpoint, focusing on the trade-offs between consistency, availability, and schema flexibility. We established that the choice of database depends entirely on the application's specific requirements. Now that you have a framework for choosing the right type of database, our next challenge is to ensure it performs well. A poorly performing database can bring even the most well-architected application to its knees.

Today, we'll tackle one of the most critical aspects of database performance. Our learning outcome is to design a database indexing strategy (e.g., B-Tree, hash) to optimize query performance. We will explore what indexes are, the data structures that power them, and the practical design principles you'll use to make your application's data access fast and efficient. This lesson will connect directly with your interest in algorithms and data structures, showing how they are applied in a core piece of system infrastructure.

1. Why Do We Need Indexes? The Problem with Full Table Scans

Imagine you have a massive table with millions of user records, and you want to find a single user by their email address. Without an index, the database has no choice but to perform a full table scan. It must load every single page of data from disk into memory and check every row, one by one, until it finds a match. This is incredibly slow and inefficient.

DB Indexing in System Design Interviews - B-tree, Geospatial, Inverted Index, and more!

To understand just how slow this can be, let's watch a brief explanation from the video 'DB Indexing in System Design Interviews'. It clearly illustrates the problem that indexing solves.

Watch from 00:30 to 02:10. Pay attention to the calculation that shows a query on a large table could take seconds or even minutes without an index. The video then introduces how an index acts as a map to solve this.

As the video explains, an index is a separate data structure that provides a fast lookup mechanism to find the location of rows in a table. It's conceptually similar to the index at the back of a book: instead of reading every page to find a topic, you look up the topic in the index, which tells you the exact page numbers.


2. The Core Index Structures: B-Tree and Hash

Databases use specific data structures to build these indexes. Understanding them is key to making good design decisions. The two most fundamental types are the B-Tree and the Hash index.

The B-Tree Index: The Workhorse of Modern Databases

The B-Tree (and its common variant, the B+ Tree) is the most widely used index structure in relational databases like PostgreSQL and MySQL. It's a self-balancing tree structure optimized for systems that read and write large blocks of data, making it perfect for disk-based storage.

Your background in algorithms will be useful here. You might be familiar with Binary Search Trees (BSTs). A B-Tree expands on this idea. While a BST node typically has two children, a B-Tree node can have many children. This creates a tree that is very wide and shallow.

B-Tree Index Finding a Row
This image shows a B-Tree search. Notice how a search for the value `15` starts at the root and makes only a few hops to find the target leaf node. The tree's shallow depth is what minimizes slow disk I/O operations.

Understanding B-Trees: The Data Structure Behind Modern Databases

To really appreciate why B-Trees are so effective for databases, let's watch 'Understanding B-Trees' from the Spanning Tree channel. It does an excellent job of explaining the data structure from first principles.

Watch from the beginning to 03:47. Focus on two key ideas: Why having more keys per node (a wider tree) is better for disk-based systems, even if it means more comparisons. How the search process works by navigating down the tree.

The key properties that make B-Trees so powerful are:

  • Sorted Data: The keys within the tree are kept in sorted order.
  • Efficiency for Multiple Query Types: This sorted nature makes B-Trees efficient for both:
    • Point queries (e.g., WHERE id = 123)
    • Range queries (e.g., WHERE created_at > '2023-01-01')

It's worth noting that most databases technically use a B+ Tree. The main difference is that in a B+ Tree, all data pointers are stored only in the leaf nodes, and the leaf nodes are linked together in a doubly linked list. This makes range scans even more efficient, as the database can find the start of the range and simply walk along the linked list of leaves to get all the required data.

The Hash Index: Optimized for Speed

A Hash Index uses a hash table. It applies a hash function to the indexed column's value and stores a pointer to the corresponding table row in the hash table.

  • Strength: Hash indexes are incredibly fast for point queries, offering an average time complexity of O(1). You simply hash the value you're looking for and immediately get the location of the data.
  • Weakness: The hash function scrambles the input, so the stored data has no inherent order. This means hash indexes cannot be used for range queries.

DB Indexing in System Design Interviews - B-tree, Geospatial, Inverted Index, and more!

Let's return to the 'DB Indexing in System Design Interviews' video for a concise explanation of hash indexes and their trade-offs.

Watch from 03:52 to 05:05. The video clearly explains how a hash index works and points out why B-Trees are generally preferred in production databases, with hash indexes being more common in in-memory stores like Redis.

Test your understanding!

You are designing the database for an e-commerce platform. You need to support two primary query patterns:

  1. Retrieve a user's profile by their unique email_address.
  2. Find all orders placed within the last 30 days based on the order_date column.

Which index type (B-Tree or Hash) would be appropriate for each column, and why?

Show answer
  1. email_address: A B-Tree index is the most practical choice. While a Hash index would be extremely fast for this exact-match query, a B-Tree is nearly as fast and much more versatile. For instance, it could also support prefix searches (e.g., WHERE email_address LIKE 'test@%') if needed in the future. Most relational DBs would default to a B-Tree here.
  2. order_date: A B-Tree index is the only suitable option. This is a range query (WHERE order_date BETWEEN NOW() AND NOW() - 30_DAYS). A Hash index cannot be used because it doesn't store data in a sorted order, making range lookups impossible.

3. How to Design an Indexing Strategy

Knowing the underlying structures is the first step. The next, more architectural step is designing an effective strategy. This isn't about memorizing rules, but about analyzing your application's data access patterns.

The Not-So-Dark Art Of Designing Database Indexes

The article 'The Not-So-Dark Art Of Designing Database Indexes' by Ben Nadel is an excellent, pragmatic guide written from a software engineer's perspective. It demystifies the design process.

Please read the following three sections from the article: Start with 'How To Design A Good Secondary Index'. This section introduces the core principle: your indexes should be designed to support your application's specific business requirements. It uses the phrase "pave the cow-paths," which is a great mental model. Next, read 'Composite Indexes And Leftmost Prefixes'. This is one of the most important practical concepts in index design. Finally, read 'Covering Indexes'. This describes a powerful optimization technique that can dramatically speed up queries.

Let's summarize the key strategic principles from that reading.

Primary vs. Secondary Indexes

  • Primary Key Index: This is the index on a table's primary key (e.g., the id column). It's typically a clustered index, meaning the physical order of rows on disk matches the index order. As the article suggests, using a simple, auto-incrementing integer is a robust and predictable strategy.
  • Secondary Indexes: These are the additional indexes you create on other columns to speed up specific queries (e.g., an index on a user_id or email column). These are usually non-clustered, meaning the index has a separate structure that contains pointers back to the data rows.
Non-Clustered/Secondary Key Index vs. Primary Key
This diagram illustrates how a non-clustered (secondary) index on `color` works. A query for 'green' cars first uses the secondary index to find the relevant `car_id`s (2, 10), and then uses those primary keys to fetch the full rows from the main table, avoiding a full table scan.

Composite Indexes and the "Leftmost Prefix" Rule

You will often need to filter by multiple columns at once (e.g., WHERE lastName = 'Smith' AND firstName = 'John'). For this, you use a composite index—an index on multiple columns.

The order of columns in a composite index is crucial. An index on (lastName, firstName) can be used for queries filtering on:

  • lastName and firstName
  • Just lastName (this is the "leftmost prefix")

However, it cannot be used to speed up a query that only filters on firstName. The database can only use the index from left to right.

Covering Indexes

A query is "covered" by an index if the index contains all the columns the query needs to retrieve. When this happens, the database can answer the query by reading the index alone, without ever needing to access the table data. This avoids an extra disk I/O step and can result in a massive performance improvement.

For example, for the query SELECT user_id, team_id FROM memberships WHERE user_id = ?, an index on (user_id, team_id) would be a covering index. An index on just (user_id) would not be.

The Design Process: Analyze and Evolve

  1. Analyze Your Queries: Identify the columns frequently used in WHERE clauses, JOIN conditions, and ORDER BY clauses. These are your primary candidates for indexing.
  2. Create Indexes: Build single-column or composite indexes to "pave the cow-paths" of your most common and performance-critical queries.
  3. Verify with EXPLAIN: Use your database's query execution planner (e.g., the EXPLAIN command in MySQL and PostgreSQL) to confirm that your queries are actually using the indexes you created. If they aren't, you may need to adjust your index or your query.
  4. Evolve: Indexing is not a one-time task. As your application's features and query patterns change, you will need to revisit and evolve your indexing strategy.

4. Beyond the Basics: Specialized Indexes

While B-Trees cover most use cases, certain types of data require specialized indexing strategies for optimal performance. Having a high-level awareness of these is part of developing an architectural mindset.

  • Full-Text Search: If you need to search for keywords within a body of text (like finding all products whose description contains "waterproof"), a B-Tree is inefficient. The solution is an Inverted Index, which maps words to the documents that contain them. This is the technology behind search engines like Elasticsearch and Lucene.
  • Geospatial Data: If you're building an application like Yelp or Uber, you'll need to query for locations within a certain radius or bounding box (e.g., "find all cafes within 2km"). B-Trees are designed for one-dimensional data and struggle with two-dimensional (latitude, longitude) queries. Specialized geospatial indexes like R-trees or techniques like geohashing are used for this.

DB Indexing in System Design Interviews - B-tree, Geospatial, Inverted Index, and more!

The 'DB Indexing in System Design Interviews' video provides a great high-level summary of these specialized cases and a useful flowchart for choosing an index.

Watch from 10:56 to the end. You don't need to memorize the details of R-trees or inverted indexes. The goal is to understand when you would need to reach for a tool beyond a standard B-Tree.


Conclusion

You have now learned how to move beyond simply using a database to strategically designing it for performance. Indexing is not magic; it is the practical application of data structures to solve the very real problem of slow data access.

Key Takeaways:

  • Purpose: Indexes are crucial for avoiding slow, costly full table scans.
  • Core Structures: The B-Tree is the versatile workhorse for most databases, handling both point and range queries efficiently. The Hash index is faster for exact-match lookups but cannot handle ranges.
  • Design Strategy: Your indexing strategy should be driven by your application's query patterns.
    • Use composite indexes for multi-column filters, paying close attention to the leftmost prefix rule.
    • Aim for covering indexes where possible to eliminate table lookups entirely.
  • Architectural Mindset: Recognize when a standard B-Tree is not enough and a specialized index (like an inverted or geospatial index) is required for the job.

Preview of the Next Lesson:
We've seen how to make a single database instance fast and efficient. But modern applications must also be resilient to failure and handle load that exceeds a single server's capacity. In our next lesson, we will explore "database replication strategies (e.g., master-slave, master-master)" to understand how data is copied across multiple servers to achieve high availability and scalability.