B-tree pages: fanout is a layout calculation
Derive leaf capacity and internal fanout from a specified page format, then connect tree height to logical and physical access costs.
On this page
The question for today
What you’ll understand
- Distinguish leaf entries from internal routing separators.
- Calculate capacity using page headers and complete entry widths.
- Explain why tree height is neither a physical-read count nor an occupancy guarantee.
A B-tree index gains its shallow shape by routing through many children per internal page. That fanout comes from concrete bytes: separators, child references, headers, and layout overhead. Begin with a stated page format rather than a memorized logarithm. Then distinguish how many keys a perfectly packed tree could hold from how a changing database actually fills its pages.
Leaves store results; internal pages route searches
In the B+ tree style used for this lesson, leaf entries contain search keys and row references or payloads, while internal pages contain separators and child references. A search compares its key with separators and descends toward the appropriate leaf. The separator convention must define boundary equality and duplicates consistently. After locating a starting leaf, sibling links can support an ordered range scan without restarting at the root for every returned entry. Different B-tree variants place records differently, so state which layout is being analyzed.
An internal page with m children commonly needs m child references and m−1 separators in a simple teaching representation. A leaf does not have that same relationship: each leaf entry includes its own complete record reference or payload. Slot arrays, tuple headers, high keys, sibling pointers, and alignment also consume bytes. Our formulas collect fixed overhead in H and assume fixed-width entries. Variable-length keys, compression, deduplication, and truncated internal separators change actual capacity; an average width is an estimate rather than a hard limit.
Full capacity and minimum occupancy are different claims
If every internal page has F children and every leaf contains L entries, a tree of h page levels can hold L times F to the power h−1 entries. This is a maximum-capacity model, not a promise that inserts always achieve those occupancies. For one count-based classical B+ policy, non-root internal pages have ceil(F/2) through F children and non-root leaves have ceil(L/2) through L entries; a non-leaf root has at least two children. Those bounds require compatible split and merge rules. Real database implementations can use different split targets, deferred deletion, or workload-specific fill policies. Do not transfer a textbook half-full invariant to every production page without checking its algorithm.
A useful estimate separates leaf fill from internal fanout. Wider included values can reduce leaf capacity without changing internal routing width in a design that excludes those values from upper levels. Skewed insertion patterns can leave some pages sparse and others frequently splitting. Keys also need a consistent total ordering under the chosen comparator or collation; violating that contract breaks navigation regardless of available space. Derive the shape from observed page statistics when capacity planning, and keep the maximum model as a sanity bound.
Height describes navigation, not storage latency
A three-level search visits three logical index pages on its descent, but the root and upper internal pages may already be cached. A leaf miss can then dominate physical I/O. A range query adds leaf pages and may fetch table rows elsewhere, while an index-only scan has additional visibility requirements in MVCC systems. CPU comparisons, cache misses, latching, and decompression remain relevant even when every page is in memory. Avoid turning a tree-height calculation into an exact elapsed-time claim.
Use the cache lab to isolate reuse. Press Reset, set capacity C=4 and stride s=4, then predict that the sixteen initial requests over addresses 0,4,8,12 produce four misses and twelve hits. Play or Step to extend that same stream. For a separate experiment, Reset, set C=3 and s=4, predict zero hits for the repeating four-address cycle, then Play or Step. This is a fully associative LRU instrument, not a B-tree or buffer manager. It shows why a shallow tree's frequently reused pages and its physical-read cost require a workload argument.
Work it through
Size a fictional three-level index
A teaching format uses 1,024-byte pages, 24 overhead bytes, eight-byte separators, eight-byte child references, and twenty-byte leaf entries. Estimate a maximally packed layout for 100,000 entries.
The internal capacity is floor((1,024−24+8)/(8+8))=63 children. Leaf capacity is floor((1,024−24)/20)=50 entries.
The leaves require ceil(100,000/50)=2,000 pages. Their parents require ceil(2,000/63)=32 pages, all of which fit under one root with capacity 63.
The idealized layout has three page levels and 2,000+32+1=2,033 index pages, excluding metadata. Lower fill or wider actual entries can increase the count; caching determines which logical visits become physical reads.
The simple format supports the proposed three-level layout. The calculation distinguishes routing fanout 63 from leaf capacity 50 rather than using one branching number for both.
The living notebook
Explore the model
Interactive lab · cache
A working set can cross the capacity boundary
Reset, set C=4 and s=4, and predict 12 hits in the initial 16 requests. Play or Step. For a separate run, Reset, set C=3 and s=4, predict zero hits, then Play or Step. The deterministic LRU stream is a reuse illustration, not a B-tree or database-buffer simulation.
- Hits / requests
- 0 / 16
- Hit rate
- 0%
- Address-stream period
- 16 addresses
A fully associative LRU cache starts empty and requests address (i·s) mod 16. Hits move an address to the newest position; misses evict the oldest when full. Playback starts with 16 requests and adds two per second, capped at 4096 explicitly simulated requests. This toy stream is not a processor-cache benchmark. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forLogical tree height and physical access cost differ when frequently reused pages remain cached.
Pause & reason
Check your understanding
Your turn
Try it yourself
Keep the example's page overhead and internal format, but widen complete leaf entries to forty bytes. Find leaf pages and page levels for 100,000 entries.
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Recalculate leaf capacity first, then divide the leaf count by fanout 63.
Compare with the solution
Leaf capacity becomes floor(1,000/40)=25, requiring 4,000 leaves. Their parents require ceil(4,000/63)=64 pages, which do not fit under a single 63-child root. One more internal level needs ceil(64/63)=2 pages under a root, giving four page levels in this packed model.
Keep the idea
Take it with you
- Leaf capacity and internal fanout follow different byte-layout constraints.
- Maximum capacity does not establish actual fill or every variant's minimum occupancy.
- Caching and table access separate tree height from physical latency.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.