Searching — a cursor descends the tree — one page I/O per tree level

A point query reads exactly ONE page per tree level — three I/Os to find one row in a tree of millions, even on a miss.

Previously

Scene 3 left you with a static tree — labeled, sized, but never used. Now we run our first SELECT through it and count pages read.

Scene 04

Searching — a cursor descends the tree

  1. Watch
  2. Try it
  3. Predict
  4. Capture
p1 (root)ROOT200400600800p4INT4080119p5INT239279319p6INT439479519p7INT639679719p8INT839879920p1712140p18416180p1981100119p20120140159p21160180199p22200220239p23240260279p24280300319p25320340359p26360380399p27400420439p28440460479p29480500519p3052054055911→I/O COUNT0DESCENT LOGtarget rowid: 42in progress…Cursor at root. Binary-search separator keys to pick a child.
What to watch for

SELECT WHERE id=42. The cursor lands on the root, binary-searches its separator keys, picks one child, and drops down. Each visited page is one disk I/O. Watch the counter.

Continue unlocks when the animation finishes.
Implementation

Highlighted lines are the ones running in the diagram right now.

Btree.search(root_page, target)
one page read per tree level — depth = I/O count
def search(root_page, target):
cursor = read_page(root_page) # +1 I/O at root
io_count = 1
while cursor.is_interior:
child_idx = binary_search(cursor.keys, target)
cursor = read_page(cursor.children[child_idx])
io_count += 1 # +1 I/O per level
return scan_leaf(cursor, target) # local scan, no I/O
Page.binary_search(keys, target)
narrow [lo, hi) until lo names the chosen child slot
def binary_search(keys, target):
lo, hi = 0, len(keys)
while lo < hi:
mid = (lo + hi) // 2
if target < keys[mid]:
hi = mid # target lives in left half
else:
lo = mid + 1 # target lives in right half
# missing keys land past every separator → rightmost child
return lo # index of the chosen child

Where this sits in Build a B-tree storage engine (SQLite-style)

Scene 04 of 11, in the Anatomy act — Pages, the tree of pages, and how a search descends.. A point query reads exactly ONE page per tree level — three I/Os to find one row in a tree of millions, even on a miss.

Up next. Lookups are cheap: 3 page reads in a tree of millions. But every INSERT must also descend to its home leaf — and what happens when that leaf is FULL? The next scene splits a leaf for the first time.

All 11 scenes in Build a B-tree storage engine (SQLite-style) · Every curriculum

Built with Arqly
Every scene in Build a B-tree storage engine (SQLite-style) builds on the one before it.All 11 Build a B-tree storage engine (SQLite-style) scenes