B-Tree & B+Tree Database Index Depth & I/O Calculator

Model database B+Tree index physical layout, internal and leaf node fanouts, tree depth ($h$), disk storage overhead, and expected random I/O query latency across PostgreSQL, MySQL InnoDB, and SQLite.

📁 Database🔒 100% Free & Private⚡ Instant Output
Quick Overview & Direct Answer

B-Tree & B+Tree Database Index Depth & I/O Calculator: Free B-Tree & B+Tree Database Index Depth & I/O Calculator tool: calculate database B-Tree & B+Tree index depth, page fanout, leaf page count, disk.

Primary Use SQL query optimization, schema formatting, relational data modeling
Input Formats SQL queries, table definitions, CSV/JSON tabular data
Output Format Beautified SQL, conversion dumps, diagram structures
Processing Mode 100% In-Browser Client-Side Processing
Access Tier Free, unlimited, no sign-up needed

Database Engine & Table Schema

rows
50 Million rows (~50M). Try 10M, 100M, or 1B rows.
bytes
PostgreSQL: 8192 (8KB); InnoDB: 16384 (16KB); SQLite: 4096 (4KB).
%
Default B-tree fill factor (70% for random inserts; 90% for sequential append).

Hardware Storage & Cache Profile

%
Percentage of index page lookups satisfied directly in RAM.

B+Tree Hierarchy & Performance Metrics

B+Tree Depth / Height (h)
3 Levels
1 Root + 1 Branch + 1 Leaf
Internal Node Fanout
357 keys
Branching factor per page
Total Index Disk Size
1.11 GB
140,056 pages
Leaf Page Fanout
357 rows
139,664 leaf pages
Expected Physical Seeks
0.15 I/Os
@ 95% RAM cache hit
Point Lookup Latency
3.0 µs
Cold cache: 60.0 µs
1,000-Row Range Scan
3 pages
Sequential leaf pointers
Key Overhead / Row
23.8 bytes
Key + Pointer + Slot + Bloat

B+Tree Multi-Level Hierarchy Diagram

■ Root Page (In RAM)    ■ Internal Branch Pages    ■ Leaf Pages (Doubly Linked)

1. The Mechanical Foundations of B-Tree Database Indexes

Modern relational and analytical database engines (including PostgreSQL, MySQL InnoDB, SQLite, Oracle, and Microsoft SQL Server) rely on the B+Tree data structure as their default general-purpose indexing architecture. Invented by Rudolf Bayer and Edward M. McCreight in 1970, and refined into the B+Tree by H. Wedekind in 1974, this self-balancing search tree was engineered specifically to solve the physical constraints of block-oriented persistent secondary storage (rotational hard disks and solid-state drives).

In memory-based software, balanced binary search trees (such as Red-Black or AVL trees) achieve optimal theoretical asymptotic search complexity of $O(log_2 N)$. However, when applied to disk-resident storage, a binary tree is catastrophically inefficient:

  • For $N = 100,000,000$ (100 million) records, a binary tree requires $log_2(10^8) approx 27$ sequential pointer dereferences.
  • On persistent storage where each node read fetches an entire disk block (e.g. 8 KB or 16 KB), traversing 27 individual disk pages on an HDD (8 ms per seek) would consume over 200 milliseconds for a single point query.
  • Even on a high-speed NVMe SSD (20 µs latency), 27 random page reads take over 540 microseconds.

The B+Tree resolves this bottleneck through massive branching fanout ($B$). By sizing each tree node to occupy exactly one physical disk page (e.g. 8,192 bytes in PostgreSQL or 16,384 bytes in InnoDB), a single page holds hundreds or thousands of index keys. This huge fanout ($B approx 300 ext{–}1,200$) squashes the tree depth down to just 3 or 4 levels for hundreds of millions of records.

2. Structural Anatomy: B-Tree vs B+Tree

While the terms "B-tree" and "B+tree" are frequently conflated in casual conversation, modern relational database engines implement the B+Tree variant exclusively for primary and secondary indexes:

Architectural Attribute Classical B-Tree (1970) Modern Database B+Tree (1974)
Data Storage Location Keys and actual data/pointers stored in both internal and leaf nodes. Internal nodes store strictly routing keys + child pointers; data stored exclusively in leaf nodes.
Internal Node Fanout Lower fanout because data payloads consume valuable internal page space. Maximum possible fanout ($B approx 500 ext{–}1200$) because only compact keys and pointers are stored.
Tree Height / Depth Taller tree for the same dataset size. Extremely shallow (typically height 3 or 4 for massive datasets).
Leaf Page Linkage Leaf nodes are isolated; no horizontal sibling pointers. All leaf nodes form a continuous doubly-linked list ($Prev leftrightarrow Next$).
Range Scan Efficiency Requires expensive recursive in-order tree traversal up and down the tree. Lightning-fast linear traversal along horizontal sibling leaf pointers.

3. Mathematical Sizing: Fanout, Height, and Page Capacity

3.1 Page Space & Usable Payload

A database page of raw size $P_{ ext{size}}$ contains fixed metadata headers (PageHeaderData, checksums, transaction flags) denoted $P_{ ext{head}}$ (e.g. 24 bytes in Postgres, 120 bytes in InnoDB). In addition, production databases enforce an operating fill factor ($FF$) to accommodate in-place updates without immediate splitting:

$$ ext{Payload}_{ ext{usable}} = (P_{ ext{size}} - P_{ ext{head}}) imes rac{FF}{100}$$

3.2 Internal Node Branching Fanout ($B_{ ext{int}}$)

An internal routing slot consists of the indexed search key ($S_{ ext{key}}$), a child page pointer ($S_{ ext{ptr}}$, typically 6 or 8 bytes), and page slot directory overhead ($S_{ ext{slot}} approx 2 ext{ bytes}$):

$$S_{ ext{int}} = S_{ ext{key}} + S_{ ext{ptr}} + S_{ ext{slot}}$$ $$B_{ ext{int}} = leftlfloor rac{ ext{Payload}_{ ext{usable}}}{S_{ ext{int}}} ight floor$$

3.3 Leaf Node Fanout ($B_{ ext{leaf}}$)

A leaf node slot stores the indexed key plus the table heap pointer (or Tuple ID / clustered primary key in secondary indexes):

$$S_{ ext{leaf}} = S_{ ext{key}} + S_{ ext{heap_ptr}} + S_{ ext{slot}}$$ $$B_{ ext{leaf}} = leftlfloor rac{ ext{Payload}_{ ext{usable}}}{S_{ ext{leaf}}} ight floor$$

3.4 Tree Height / Depth Formulation

The total number of leaf pages required to store $N$ table rows is:

$$L = leftlceil rac{N}{B_{ ext{leaf}}} ight ceil$$

The total height of the B+Tree ($h$) is determined by the internal fanout required to index all $L$ leaf pages:

$$h = 1 + leftlceil log_{B_{ ext{int}}} (L) ight ceil$$

where $h=1$ represents a tiny index fitting entirely in a single root page.

4. Disk I/O & Query Latency Modeling

When executing a point lookup query (e.g. SELECT * FROM users WHERE id = 12345;), the database traverses the B+Tree from the root page down to the specific leaf page, incurring $h$ page inspections:

$$ ext{Total Pages Accessed} = h ext{ (Index Pages)} + 1 ext{ (Heap Data Page, if not index-only)}$$

4.1 The Buffer Pool Cache Effect

In production database servers, the root page and top intermediate branch levels reside almost permanently in the database RAM buffer pool (Postgres shared_buffers or InnoDB Buffer Pool):

  • Level 1 (Root Page): Consists of exactly 1 page ($approx 8 ext{ KB}$). Cache hit probability $approx 100%$.
  • Level 2 (Top Intermediate Branch): Consists of $B_{ ext{int}}$ pages ($approx 300 ext{–}1000$ pages $approx 2.4 ext{–}8 ext{ MB}$). Cache hit probability $approx 99.9%$.
  • Leaf Level: Consists of tens of thousands of pages. Physical disk seeks occur primarily when accessing random cold leaf pages.

The expected number of physical disk I/O operations for a point query is:

$$ ext{Expected Disk I/Os} = h imes (1 - ext{CacheHitRatio})$$ $$ ext{Expected Latency} = ext{Expected Disk I/Os} imes ext{SeekLatency}_{ ext{storage}} + h imes ext{RAM_Latency}$$

5. The UUID Primary Key Anti-Pattern: Page Splits & Bloat

One of the most catastrophic database architecture anti-patterns is using completely random 128-bit UUIDs (such as standard UUID v4) as clustered B-tree primary keys. The physics of B-tree page allocation explains why:

  • Sequential Keys (BIGINT / AUTO_INCREMENT / UUIDv7): Successive values are strictly greater than prior values. New records append monotonically into the rightmost leaf page. Once full, a clean page allocation occurs, maintaining an optimal ~90% to 95% fill factor with zero fragmentation.
  • Random Keys (UUIDv4): New keys distribute with equal statistical probability across all leaf pages. When an arbitrary page fills, the database must execute a 50/50 Page Split: allocating a new page, moving half the keys to the new page, and updating parent pointers.
  • Catastrophic Consequences: Average page fill factor collapses from 90% down to ~67% (a 33% bloat penalty). The physical index footprint expands by 50%, cache thrashing destroys buffer pool hit rates, and write amplification degrades NVMe SSD lifespan.

6. Worked Mathematical Sizing: 50 Million Row Index

Database: PostgreSQL, Page Size $P_{ ext{size}} = 8,192 ext{ bytes}$, Header $P_{ ext{head}} = 24 ext{ bytes}$, Fill Factor $FF = 70%$. Table Size $N = 50,000,000 ext{ rows}$. Key: BIGINT (8 bytes). Heap Pointer: Postgres TID (6 bytes). Slot directory overhead: 2 bytes.

Step 1: Usable Payload per Page

$$ ext{Payload}_{ ext{usable}} = (8,192 - 24) imes 0.70 = 8,168 imes 0.70 = 5,717 ext{ bytes}$$

Step 2: Fanout Calculations

$$S_{ ext{int}} = 8 ext{ (key)} + 6 ext{ (child ptr)} + 2 ext{ (slot)} = 16 ext{ bytes} implies B_{ ext{int}} = leftlfloor rac{5,717}{16} ight floor = 357 ext{ keys/page}$$ $$S_{ ext{leaf}} = 8 ext{ (key)} + 6 ext{ (TID)} + 2 ext{ (slot)} = 16 ext{ bytes} implies B_{ ext{leaf}} = leftlfloor rac{5,717}{16} ight floor = 357 ext{ rows/page}$$

Step 3: Leaf Pages & Tree Height

$$L = leftlceil rac{50,000,000}{357} ight ceil = 140,057 ext{ leaf pages}$$ $$ ext{Level 2 (Parent of Leaves)} = leftlceil rac{140,057}{357} ight ceil = 393 ext{ branch pages}$$ $$ ext{Level 1 (Root)} = leftlceil rac{393}{357} ight ceil = 2 implies ext{Requires 1 Root page indexing the 2 branch pages}$$ $$ ext{Tree Height } h = mathbf{3 ext{ Levels (Root + Branch + Leaf)}}$$

Step 4: Total Index Size

$$ ext{Total Pages} = 1 ext{ (Root)} + 393 ext{ (Branch)} + 140,057 ext{ (Leaf)} = 140,451 ext{ pages}$$ $$ ext{Disk Size} = 140,451 imes 8,192 ext{ bytes} = 1,150,574,592 ext{ bytes} approx mathbf{1.15 ext{ GB}}$$

Step 5: Query Latency

On an NVMe SSD (20 µs seek latency) with a 95% buffer pool hit ratio:

$$ ext{Expected Disk I/Os} = 3 imes (1 - 0.95) = 0.15 ext{ physical reads}$$ $$ ext{Average Latency} = 0.15 imes 20 ext{ }mu ext{s} = mathbf{3.0 ext{ microseconds}}$$

Even for 50 million records, a point query finishes in 3 microseconds!

Frequently Asked Questions

In a balanced Binary Search Tree (like an AVL or Red-Black tree), each node has a fanout of only 2, resulting in a tree depth of log2(N). Searching 100 million records would require ~27 node traversals. In magnetic or SSD disk storage, 27 random page reads would take tens of milliseconds. A B+Tree matches node size to disk page blocks (e.g. 8KB or 16KB), achieving fanouts of 500 to 1,200 keys per page. This massive fanout compresses the depth of 100 million records down to just 3 or 4 page reads.

In a classic B-Tree, both internal branch nodes and leaf nodes store full data records (or heap row pointers). In a B+Tree, internal nodes store strictly key-pointer routing pairs, leaving data records or row pointers exclusively in the leaf level. Furthermore, all leaf pages in a B+Tree are linked sequentially in a bidirectional doubly-linked list. This maximizes internal node fanout (keeping the tree shallow) and enables lightning-fast horizontal range scans without backtracking up the tree.

Monotonically increasing keys (such as AUTO_INCREMENT or BIGSERIAL) append predictably to the rightmost leaf page, achieving near 100% page fill efficiency. In contrast, random UUIDs (v4) insert uniformly across the entire keyspace. When an arbitrary leaf page fills up, the database must execute a costly page split (50/50 partition), dropping average page fill factor to ~67%. This bloats index storage size by 30-50% and thrashes the database buffer pool cache.

A point lookup requires traversing h pages (from root down to the leaf). In modern database servers, the root page and top intermediate levels (depth 1 and 2) reside permanently in RAM buffer pool cache. Only the leaf page (and the heap table row page, if not using a covering index) may require physical disk I/O. Therefore, a tree with height h = 3 typically costs 0 to 1 physical disk read (under ~50-100 µs on NVMe SSD).