Unit 6 · Databases · Lesson 5 of 10
Buffer pools, page layouts, row and column stores
Step 1: Learn the ideas
- Files, pages and page directories
- Slotted pages, record IDs and tuple headers
- Variable-length values and overflow storage (for example TOAST)
- Buffer pool: page table, frames, pin counts and dirty flags
- Replacement policies: LRU, clock, LRU-K, ARC and scan resistance
- Why not mmap: eviction control, write-back order and error handling
- Row (NSM), column (DSM) and hybrid (PAX) storage models
- Columnar compression: dictionary, run-length, bit-packing, delta
- Open columnar formats: Parquet row groups, column chunks and statistics
Why it matters
Every query eventually reads pages. Whether a scan reads 40 GB or 2 GB, and whether those bytes come from memory or storage, is decided here. Buffer-pool behavior also explains everyday puzzles: why the first run of a query is slow, and why one large report can evict everyone else's working set.
Core ideas
Pages and slotted layout. Tables live in files divided into fixed-size pages (8 KB by default in PostgreSQL, 4 KB by default in SQLite). A slotted page has a header, an array of slots pointing to tuples, and tuples packed from the end of the page, with free space between. A record ID is (page, slot), so a tuple can move within its page without changing its ID. Large values go to overflow pages or side tables (TOAST in PostgreSQL).
Buffer pool. A fixed array of frames plus a page table from page ID to frame. Callers pin a page while using it; only unpinned pages can be evicted, and a dirty page may be written back only after the log records covering it are durable. Plain LRU suffers from sequential flooding, where one large scan evicts the hot pages; clock, LRU-K, ARC and small ring buffers for bulk scans are common answers. A DBMS prefers its own pool to mmap because it must control eviction and write-back order, prefetch using knowledge of the plan, and handle I/O errors explicitly; with mmap the kernel decides when dirty pages reach storage and reports I/O errors as signals.
Row versus column. A row store keeps a tuple's attributes together, so one page read fetches a whole record: good for OLTP point reads and updates. A column store keeps each attribute contiguous: a query touching 2 of 50 columns reads about 2/50 of the data, values of one type compress well, and execution can loop over arrays of one column. PAX keeps a page's rows together but groups each column inside the page; Parquet's row groups and column chunks apply the same idea at file scale, with per-chunk statistics that let readers skip data.
A back-of-envelope check: 100 million rows with 50 eight-byte columns is about 40 GB stored by row. Summing one column filtered on another reads about 1.6 GB stored by column, before compression.
Compression. Dictionary encoding replaces values with small integer codes; run-length encoding stores (value, count) pairs and shines on sorted columns; bit-packing stores small integers in few bits; delta encoding stores differences between neighboring values. Column engines often evaluate predicates directly on encoded data and build full tuples as late as possible.
Common misconceptions
- "The OS page cache makes a buffer pool unnecessary." The DBMS needs control over eviction and flush order for correctness and predictable performance.
- "LRU is the best policy." Sequential scans defeat it; real systems add scan resistance.
- "Column stores are always faster." Single-row inserts and updates are expensive, and rebuilding wide rows costs work; they win on scans of few columns.
- "Compression always costs speed." For scans it often saves time by cutting I/O and memory traffic, especially when encoded data can be filtered directly.
How to approach it
Draw a slotted page and walk through insert, delete and compaction. Then build or read a buffer pool; BusTub's buffer pool project exercises pinning, replacement and concurrency. Finally, estimate bytes scanned for the same query in row and column layouts and connect the result to query execution.
Suggested order
- 1Read CMU 15-445 Fall 2025 Lecture #03 notes (Database Storage I) for pages, page layout and record IDs.
- 2Read Lecture #04 notes (Memory Management) for the buffer pool, replacement policies and why the DBMS manages memory itself.
- 3Read Lecture #06 notes (Storage Models & Compression), then the PostgreSQL Database Page Layout page to see a real slotted page.
- 4Read the CIDR paper on mmap in database systems for the correctness and performance arguments behind a DBMS-managed buffer pool.
- 5Read “Column-Stores vs. Row-Stores: How Different Are They Really?” to see which column-store techniques account for the speed difference.
- 6Implement BusTub Project #1 (Buffer Pool Manager), or a small buffer pool with pin counts, an LRU-K or clock policy and tests.
Step 2: Check off what you can do
Tap each one you can genuinely do or explain. Each earns 10 XP.
Step 3: Go deeper
CMU 15-445/645 Intro to Database SystemsStart here
Carnegie Mellon University Database GroupSection: Fall 2025 Lecture #04: Memory Management (notes)
Buffer pool structure, replacement policies and why the DBMS avoids the OS cache; Lectures #03 and #05 cover page layout and log-structured storage.
Open(opens in a new tab)Link responded, 5 Oct 2026CMU 15-445/645 Intro to Database Systems
Carnegie Mellon University Database GroupSection: Fall 2025 Lecture #06: Storage Models & Compression (notes)
Row, column and hybrid storage models and columnar compression schemes.
Open(opens in a new tab)Link responded, 5 Oct 2026PostgreSQL documentation (current release)
PostgreSQL Global Development GroupSection: Database Page Layout
A real slotted-page format: page header, item identifiers, free space and tuple headers.
Open(opens in a new tab)Link responded, 5 Oct 2026Are You Sure You Want to Use MMAP in Your Database Management System?
CIDREvidence for why relying on mmap instead of a DBMS-managed buffer pool causes correctness and performance problems.
Open(opens in a new tab)Link responded, 5 Oct 2026Column-Stores vs. Row-Stores: How Different Are They Really?
University of Maryland (author-hosted PDF)Which column-store techniques (compression, late materialization, block iteration) explain the performance gap with row stores.
Open(opens in a new tab)Link responded, 5 Oct 2026
Step 4: Practice
Warm up with LRU Cache, then implement the buffer pool with tests for pin and unpin, eviction order, dirty write-back and concurrent access. Add a small experiment: run a large sequential scan next to a hot point-lookup workload and compare hit rates under LRU and a scan-resistant policy. Separately, estimate bytes read for a two-column aggregate under row and column layouts, then check the estimate by converting a CSV file to Parquet and timing the query. The course’s current offering may have an updated buffer pool project.
- CMU 15-445 Fall 2025 Project #1: Buffer Pool Manager (BusTub)Open(opens in a new tab)Not attemptedCMU 15-445
- LRU CacheLeetCode(opens in a new tab)Not attemptedLeetCode
- CMU 15-445 BusTub projects (buffer pool, index, query execution, concurrency control)Open(opens in a new tab)Not attemptedCMU 15-445
Record attempts on each problem’s page (+15 XP, +10 more when solved on your own).
Step 5: Test yourself
Answer each one out loud or on paper before you finish the lesson.
- Question 1What could go wrong if a page were evicted while a thread still used it?
- Question 2Why must the log be flushed before a dirty page is written back?
- Question 3How does sequential flooding hurt LRU, and what are two fixes?
- Question 4When does PAX beat both pure row and pure column layouts?
- Question 5Why does run-length encoding work better on sorted columns?
You’re done when you can
- Draw a slotted page and explain insert, delete and compaction without changing record IDs.
- Implement a buffer pool that passes tests for pinning, eviction and dirty write-back.
- Estimate bytes scanned for a query in row and column layouts within a factor of two of a measurement.
- Explain in under two minutes why a DBMS avoids mmap for its main data files.
Finish the lesson
Unlocks next: Query execution: joins, aggregation, vectorization


