The Brute Force Answer, and Why It Breaks

Before getting into how databases are fast, it helps to understand the naive alternative.

Imagine your data is stored in a plain flat file, row after row, no structure beyond that. You want to find every customer whose last name is "Nguyen." The computer has one option available to it. Start at the top. Read every single row. Check if the name matches. Keep going until the file runs out. In computer science, this is called a full table scan, and it is exactly as slow as it sounds. On a table with a hundred million rows, that is a hundred million comparisons, every single time, for every single query.

image.png

Sorting the data helps some. If the rows are sorted alphabetically by last name, you can use binary search. Split the dataset in half, check the middle, decide which half the answer lives in, and repeat. That turns a hundred million comparisons into roughly twenty seven. The math is genuinely beautiful. But sorting only helps when you are searching on the column you sorted by. Sort by last name and your queries on email address are back to scanning every row. Sort by email address and last name is slow again. You can only sort one way at a time, and real applications search on dozens of different columns.

Something else was needed. Something that could answer arbitrary queries on arbitrary columns, at arbitrary scale, without scanning the whole table every time. The answer that emerged, and that still underlies almost every database you have ever used, is a tree.

Binary Search Trees, and Where They Fall Apart

A binary search tree is a structure most programmers encounter early. Every node holds a value, a left child, and a right child. Values smaller than the node go left, values larger go right, and the whole thing stays ordered so that searching becomes a series of left or right decisions rather than a full scan. Find the root, compare, go left or right, compare again, repeat until you arrive at the answer. Fast, elegant, and completely impractical for databases.

Why you may ask? Well, the problem here is not the logic. The logic itself is already sound and clear. The actual problem here lies in the hardware.

A binary search tree in memory works beautifully because reading any node is nearly instant. But databases do not live in memory. They live on disk, spinning hard drives or SSDs, and reading from disk is orders of magnitude slower than reading from RAM. Every time a tree node sits on a different part of the disk, reading it costs a full disk seek. A binary search tree with a million nodes can be thirty levels deep, meaning a single search might require thirty separate disk reads. Thirty disk seeks. On hardware where each seek takes milliseconds, that adds up to something embarrassingly slow.

The database world needed a tree that could answer searches in far fewer disk reads. The answer was to make each node much, much fatter.

image.png

B-Trees, the Structure That Changed Everything

image.png

A B-tree solves the disk problem by changing the shape of the tree entirely. Where a binary search tree keeps exactly two children per node, a B-tree node can hold hundreds or even thousands of keys, with a corresponding number of child pointers. The tree grows wide instead of tall. A B-tree with millions of entries might only be three or four levels deep, meaning finding any record requires at most three or four disk reads instead of thirty.

The mechanics work like this. Each node in a B-tree holds multiple keys in sorted order, with child pointers sitting between them. If a node holds the keys 10, 20, and 30, then the first child pointer leads to values less than 10, the second to values between 10 and 20, the third to values between 20 and 30, and the fourth to values greater than 30. Searching means loading one node, scanning its keys, following the right pointer, loading the next node, and so on down the tree. Because each node is sized to match a disk page, typically 4 or 16 kilobytes, each step costs exactly one disk read, and the tree is shallow enough that the whole search finishes in a handful of reads.

Insertions and deletions keep the tree balanced automatically. When a node fills up, it splits in two and pushes a key up to the parent. When nodes get too sparse, they merge. The tree never degenerates into a lopsided mess the way an unbalanced binary tree can. That self-balancing behavior is part of why B-trees became the default choice. They perform predictably under all kinds of workloads, which is exactly what a database engine needs.

B+ Trees, and the Refinement That Stuck

image.png

B-trees were a major leap forward, but the database world landed on a variation called the B+ tree, and understanding the difference explains a lot about how modern databases behave.