B-Trees and B+ Trees
A billion keys.
How many disk reads?
Start with the lecture’s problem: a balanced tree can still make too many trips to disk. Follow the index, split a full node, then discover why B+ leaves stay linked.
14.1From binary trees to m-way search trees
Balance controls the path length. What if every step costs a disk read?
Pack more keys into each page
Minimum levels at full capacity, using N ≤ mh−1. Count one read per visited page, with no cache. Real occupancy can add levels. The latency is a teaching assumption, not a device benchmark.
An m-way search tree stores at most m−1 ordered keys per node. A node containing k keys has k+1 child-pointer positions, partitioning the search space into k+1 ordered ranges; some pointers may be null. Here all keys are distinct. A match at an ordinary search-tree node ends the search there.
A 4-way search-tree node
Root keys [10,30,35] have four child-pointer slots. The children show actual keys, with their permitted ranges below them.
Read this diagram as text
If each node occupies a disk page, a binary tree may require many page reads along a long path. Keeping many separators in one page reduces the number of levels.
Balanced binary trees have O(log₂ N) height. A wide balanced tree has O(logm N) height when its branching factor is proportional to m. A cached root does not require a disk read.
Why the level count works: derivation and full-capacity table
Let h be the number of levels, counting the root as level 1. In a perfectly filled m-way tree, every internal node has m children and every node has m−1 keys:
keys = (m−1) × nodes = mh−1
| Levels h | Keys: m=2 | Keys: m=200 |
|---|---|---|
| 3 | 7 | 7,999,999 |
| 5 | 31 | 319,999,999,999 |
| 7 | 127 | 12,799,999,999,999,999 |
14.2Indexing and multilevel indexing
You need Chapter Five. Do you open chapters one by one?
Find Chapter Five
The book is closed.
Where will you look first?
Contents
- Chapter One1
- Chapter Two15
- Chapter Three45
- Chapter Four51
- Chapter Five72
- Chapter Six90
Try checking chapters, then use the contents entry. These six entries come from the lecture’s book illustration.
See the contents-page illustration

For a data file sorted by a distinct search key, a sparse index can store one entry per data block: the minimum key in that block and a pointer to it. Search chooses the entry with the largest key not exceeding the requested key. If the index spans several pages, build another sparse index over those pages.
Follow an index entry to its page
Example: 30 sorted records; five records per data block; at most three entries per index block. Search for record 23.
Read this diagram as text
The root R has two entries, one for each first-level index page. I1 and I2 each have three entries, one for each data block they cover. These are minimum-key/pointer pairs, so an index page with k entries has k pointers; it is not drawn using the k+1 separator-pointer layout of a B-tree node.
A concentric recording path on a magnetic disk surface.
An addressable segment of a disk track. SSDs do not have physical tracks.
A database page is an I/O unit; a file-system cluster is an allocation unit containing one or more sectors. They need not have the same size.
14.3Index-capacity exercises
Two small divisions decide how many pages an index needs.
Try the lecture questions first. Open the worked solutions when you are ready to check your reasoning.
Sparse-index example
30,000 sorted records; record size 100 B; block size 1024 B; key size 9 B; block pointer 6 B.
Reveal the worked solution and page-read comparison
data blocks = ⌈30,000/10⌉ = 3,000
index entry size = 9 + 6 = 15 B
index entries per block = ⌊1024/15⌋ = 68
first-level index blocks = ⌈3,000/68⌉ = 45
root index blocks = ⌈45/68⌉ = 1
| Access method | Worst-case page reads | Assumption |
|---|---|---|
| Sequential scan of data | 3,000 | No useful ordering or index |
| Binary search on data blocks | 12 | Sorted, directly addressable blocks |
| Binary search on 45 index blocks, then data | 6 + 1 = 7 | One index level; the selected block is scanned in memory |
| Two-level sparse index, then data | 1 + 1 + 1 = 3 | Root → first-level page → data page |
For ordinary binary search over N candidate pages, ⌊log₂ N⌋+1 is a worst-case probe bound for N ≥ 1. These are not average search costs. An average requires a stated query distribution and search algorithm.
Dense multilevel-index example
16,384 records; record size 32 B; block size 1024 B; key size 6 B. Assume both record locators and upper-level block pointers occupy 10 B.
Reveal all three index levels
Block capacity
- Records per data block
- 1024 ÷ 3232 records
- Data blocks
- 16,384 ÷ 32512 blocks
- Dense index entry size
- 6 + 1016 B
- Index entries per block
- 1024 ÷ 1664 entries
Build the index
- 256blocks
First level
One entry per record
⌈16,384 ÷ 64⌉ - 4blocks
Second level
One entry per first-level block
⌈256 ÷ 64⌉ - 1root block
Third level
One entry per second-level block
⌈4 ÷ 64⌉
A complete uncached lookup traverses three index pages, then the data page: four page reads. The dense first level has one entry per record; the upper levels are sparse indexes over index pages.
14.4B-tree structure and order
Order 5 allows five children. How many keys fit?
When does this order-5 leaf need repair?
Read the complete B-tree rules and root exceptions
- Every internal node with k keys has exactly k+1 non-null child pointers. The keys separate strictly ordered child ranges.
- Every non-root node stores ⌈m/2⌉−1 through m−1 keys. Thus non-root internal nodes have ⌈m/2⌉ through m children.
- An internal root has at least two children and one key. A leaf root may store 0 through m−1 keys; zero keys represents an empty tree.
- All leaves are at the same depth. Leaves have no child subtrees.
- In the indexed representation used here, every key has a record pointer, including keys stored in internal nodes.
P₀, ⟨K₁,RP₁⟩, P₁, …, ⟨Kk,RPk⟩, Pk
Keys in Pᵢ lie strictly between the adjacent separators.
| Order-5 node | Children | Keys |
|---|---|---|
| Internal root | 2…5 | 1…4 |
| Non-root internal node | 3…5 | 2…4 |
| Non-root leaf | 0 | 2…4 |
| Leaf root | 0 | 0…4 |
Order from a block-size constraint
Key = 9 B; record pointer = 7 B; child block pointer = 6 B; block = 512 B. An internal node with capacity p child pointers has p−1 key/record-pointer entries.
22p ≤ 528 ⇒ p ≤ 24
The maximum order is 24: p=24 occupies exactly 512 B; p=25 would occupy 534 B. Real page overhead can lower this capacity.
Maximum records in four full levels
For order 23, every full node stores 22 keys; each full internal node has 23 children. Counting the root as level 1:
This counts each B-tree key/record-pointer entry once, including entries in internal nodes.
14.5B-tree insertion
Insert 55. Which key gets promoted?
The lecture’s leaf [50,56,57,58] has no spare slot. Inserting 55 produces [50,55,56,57,58]. Make your prediction, then step through the split.
Compare with the original insert-55 diagram
![Insertion example: root [20,30], its right child [35,48,60,68], and the target leaf [50,56,57,58].](assets/chapter14/slide19-insert55.png)
Search for the key first. If it is absent, insert its entry into the appropriate leaf in sorted order. An overflowing node has too many keys. Split it around a median, move the median entry to the parent, and reconnect all child pointers. Repeat upward if necessary. Splitting the root creates a new root and adds one level.
Insert 55 into an order-5 B-tree
Maximum four keys per node. Every child and record-pointer cell is shown; red marks temporary overflow.
Read this diagram as text
Connect the animation to insertion pseudocode
insert(tree, entry):
leaf = search_for_absent_key(tree, entry.key)
insert_sorted(leaf.entries, entry)
node = leaf
while node has more than m - 1 entries:
parent = node.parent
left, median_entry, right = split(node)
if parent is null:
tree.root = new_root(median_entry, left, right)
return
replace node in parent by left, median_entry, right
node = parent
The split transfers child pointers along with their corresponding key ranges. The promoted entry is removed from its old node; it is not duplicated in a B-tree leaf.
Order-4 construction exercise
Insert 2, 5, 10, 1, 6, 9, 4, 3, 12, 18, 20, 25. Here use top-down insertion: split any full three-key node before descending into it. This produces the following final tree. A bottom-up split convention can produce a different valid tree.
Final order-4 construction
Root [5]; internal children [2] and [9,12]; all twelve inserted keys appear exactly once.
Read this diagram as text
14.6B-tree deletion
A leaf loses a key. Can its sibling lend one?
The two deletion examples start from the same tree. Delete 10 to see a rotation through the parent. Delete 3 to see why a merge is sometimes necessary.
For a leaf key, remove the entry directly. For an internal key, replace its entry with its predecessor (largest key in the left subtree) or successor (smallest key in the right subtree), then remove that replacement at its leaf. The examples below use bottom-up repair after deletion.
Delete 10: repair by borrowing
Order 5 requires at least two keys in every non-root node. Borrowing rotates entries through the parent separator.
Read this diagram as text
Delete 3: repair by merging
An independent example starting from the original tree. The adjacent sibling cannot lend.
Read this diagram as text
An adjacent sibling with more than the minimum can lend. A parent separator moves down; a sibling entry moves up. For internal nodes, transfer the matching child pointer as well.
If no adjacent sibling can lend, combine the deficient node, a parent separator, and an adjacent sibling. Remove that separator and one child pointer from the parent.
If the parent underflows, repeat the repair above it. If an internal root becomes empty, replace it with its only child, reducing the height by one.
With this repair-after-deletion method, merging a one-key-deficient node and a minimum-size sibling fits within the node capacity. For order 5: 1 + 1 + 2 = 4 keys. No overflow split is needed during the repair.
14.7B+ tree structure
You found 50. How do you read the next records?
Route the search
Separator keys tell you which child to follow. Their record pointers are gone, leaving room for more children.
Read in order
Find the first record, then use the next-leaf link to continue the range scan.
Use internal order m and a separate leaf capacity q. Non-root internal nodes have ⌈m/2⌉…m children; with the half-full convention, non-root leaves have ⌈q/2⌉…q entries. An internal root has 2…m children. A leaf root has 0…q entries. All leaves have the same depth.
Leaf: ⟨K₁,RP₁⟩, …, ⟨Kq′,RPq′⟩, next
q′ is the current number of entries, at most q.
B+ point search and linked-leaf scan
This example has internal order m=3 and leaf capacity q=3. Each separator is the minimum key in its right subtree.
Read this diagram as text
- With this separator convention, equality follows the pointer to the right of the matching separator. For [30,60], P0 covers k<30, P1 covers 30≤k<60, and P2 covers k≥60.
- Record pointers r5, r10, … appear only in leaves. Internal separator copies do not represent extra data records.
- After finding the first qualifying leaf, a range scan follows next-leaf pointers in order. The final next pointer is null.
B+ insertion and deletion
Insert at a leaf. If a leaf splits, keep all data entries in the two leaves, copy the minimum key of the new right leaf into the parent, and repair the next-leaf links. If an internal node splits, move its separating key up and redistribute child pointers. Splitting can propagate to the root.
Delete at the leaf. Borrow or merge if its occupancy falls below the chosen minimum, update affected separator keys to preserve the routing convention, and repair leaf links after a merge. Repair deficient ancestors as needed; an empty internal root is replaced by its only child.
14.8B+ tree capacity calculations
Same 512-byte page. Why do B+ internal nodes fit more children?
Dropping record pointers from internal nodes makes room for ten more child pointers in this exercise. Page headers are excluded, as in the slides.
Key = 9 B; record locator = 7 B; block pointer = 6 B; block size = 512 B. Ignore page overhead and reserve one next-leaf pointer per leaf.
15p ≤ 521
pmax = ⌊521/15⌋ = 34
34 child pointers and 33 separators; 501 B used. A 35-child node would require 516 B.
16q ≤ 506
qmax = ⌊506/16⌋ = 31
31 key/record-pointer entries and one next pointer; 502 B used. A 32-entry leaf would require 518 B.
Here leaf capacity q counts data entries, and internal order p counts child pointers. Keeping these quantities separate avoids the ambiguous term “leaf order.”
Count records in four levels
The root follows the same occupancy assumption. Internal separator copies are excluded.
Why “70% full” needs a rounding convention
Four levels and 70% occupancy
Let the root be at level 0 and the leaves at level 3. Capacities are 34 children per internal node and 31 entries per leaf. Exactly 70% at every node is impossible because 0.7×34=23.8 and 0.7×31=21.7 are not integers. State the rounding rule before computing.
| Interpretation | Children per internal node | Entries per leaf | Leaf data entries |
|---|---|---|---|
| At least 70% full; uniform minimum integer occupancy | ⌈23.8⌉ = 24 | ⌈21.7⌉ = 22 | 24³ × 22 = 304,128 |
| Textbook estimate using floors at every node | ⌊23.8⌋ = 23 | ⌊21.7⌋ = 21 | 23³ × 21 = 255,507 |
Both rows assume the same occupancy for every internal node, including the root. The floor estimate is actually about 67.6% of internal child capacity and 67.7% of leaf capacity. If 70% describes only an average, the exact record count is not determined without more information. Separator copies are not counted as data entries.
14.9B-tree versus B+ tree
What does the “+” change for a range query?
| Aspect | B-tree | B+ tree |
|---|---|---|
| Record entries in this index model | Internal nodes and leaves | Leaves only |
| Equality search | May finish in an internal node | Reaches the data entry at a leaf |
| Ordered range traversal | In-order traversal can visit internal nodes and leaves | Initial search, then follow the leaf chain |
| Internal capacity for equal page/field sizes | Record pointers consume internal space | More space available for separators and child pointers |
| Balance | All leaves at the same depth | All leaves at the same depth |
Both are balanced multiway trees designed to reduce page accesses. B+ trees are particularly useful for ordered scans because the data entries occupy a linked sequence of leaves.
Applications and implementation choices
Database and file-system indexes use B-tree-family structures in several forms. MySQL documents InnoDB’s ordinary clustered and secondary indexes as B-trees. Clustered-index leaves contain row data; secondary-index leaves contain indexed keys and primary-key locators. Spatial indexes use R-trees. Do not infer the exact variant from a product category alone.
LevelDB and RocksDB use log-structured merge-tree designs. They should not be listed as examples of B+ tree storage engines.
Lecture source and further reading
References and source slides
- Gordon College — B-tree definitions, insertion, and deletion
- OpenDSA — B/B+ tree nodes, splits, and leaf links
- UC Berkeley CS 186 — B+ tree search and copy-up versus move-up
- MySQL manual — physical structure of InnoDB indexes
- LevelDB — implementation and sorted-table levels
- RocksDB — LSM-tree overview
The linked PowerPoint is the original lecture source. Use the corrected definitions, diagrams, and calculations on this page when they differ from those slides.