Build a B-tree storage engine (SQLite-style) (11 scenes)
Scene 02 · The file is a strip of pages
The DB file is a strip of identical fixed-size pages — every read/write is one whole page because the disk and OS work in pages.
Previously
We need an ordered structure. Before we can build it, we have to know what the storage substrate looks like — and it's not a sea of bytes.
Scene 02
The file is a strip of pages
Diagram
**users.db** is rendered as a horizontal strip of identical rectangles labeled **page 1**, **page 2**, …, **page N** (each 4096 bytes by default). Page 1 carries the **file header** flag. A second lane below shows the OS's own **4 KB blocks**; the two lanes line up at 4 KB and visibly mis-align at 1 KB or 64 KB. A **read-head** lands on a page; one click = one logical disk I/O.
The file isn't a soup of bytes — it's a strip of identical pages. Watch the read-head sweep across them; each landing is one disk I/O. Then an INSERT picks exactly ONE page as rowid 42's home.
Implementation
Pager.read_one_row
fetch the WHOLE page; slice the row out in RAM
1def read_one_row(file, page_size, page_no, row_offset):2 # disk addresses are page numbers, not byte offsets3 offset = (page_no - 1) * page_size4 page_buf = read(file, offset, page_size) # one I/O = one page56 # row extraction happens entirely in RAM7 row = extract_row_from_page_buffer(page_buf, row_offset)8 return row
Kernel.effective_io
bytes the kernel actually moves for one page read
1def effective_io(row_size, page_size, os_block=4096):2 if page_size < os_block:3 # sub-block page: kernel still drags a whole 4 KB block,4 # SQLite throws the rest away5 sub = os_block // page_size6 return os_block # waste = (sub - 1) * page_size per read78 if page_size == os_block:9 # aligned: one page = one block = one disk transfer10 return os_block1112 # supra-block page: one logical read spans many blocks13 blocks = page_size // os_block14 return blocks * os_block # bandwidth = blocks * 4 KB per row