Step 1 of 3 · Reading · ~4 min
Buffer Pool and LRU Eviction
Page-Based Storage
Page Cache: A Buffer Pool with LRU Eviction
Reading a page from disk costs thousands of times more than reading it from memory. A database therefore keeps a buffer pool: a fixed number of in-memory slots (frames) holding recently used pages, between the B-tree and the disk. After this lesson your pool answers reads from memory when it can, goes to disk when it must, decides which page to throw out when full, and never loses a change.
Hit, miss, load
Every READ of page p is one of:
- hit:
pis in the pool. Return it; it is now the most recently used page. - miss, page on disk: count a miss, make room if the pool is full, load
pas the most recently used page and return it. - miss, no such page: count a miss and answer
NOT_FOUND. Nothing is loaded.
Which page to evict: least recently used
Pages used recently are likely to be used again soon (the root of a B-tree is read by every search), so when the pool is full the victim is the page untouched for the longest. The simplest exact structure is a list ordered from least to most recently used: a touch moves a page to the end, the victim is taken from the front. Real systems use a hash map plus a linked list to make touches O(1); the order they implement is the same.
capacity 3 order (LRU -> MRU)
read 1,2,3 1 2 3
read 1 (hit) 2 3 1
read 4 (miss) evict 2: 3 1 4
Dirty pages and write-back
A WRITE changes the cached copy only. The page is now dirty: memory and disk disagree. If the pool dropped it silently, the change would vanish, so evicting a dirty page first writes it back to disk. A clean page can be dropped for free. Writes put the new contents in the pool without reading the old ones from disk (the whole page is replaced), so a write is neither a hit nor a miss.
CACHE FLUSH writes back every dirty page, in ascending page number, and reports how many (OK flushed=<n>). Afterwards those pages are clean, so flushing again writes nothing.
Pinning
A page that is in use (an operation is walking it) must not be evicted from under the caller. CACHE PIN <id> raises a pin count and CACHE UNPIN <id> lowers it. The victim is the least recently used page whose pin count is 0. If every page is pinned, the pool cannot make room: ERR all pages pinned, and nothing changes. Pins nest: two pins need two unpins. Pinning does not change recency.
Commands
| Command | Output |
|---|---|
DISK WRITE <id> <data> | OK (puts data straight on disk, for test setup) |
DISK READ <id> | the data, or NOT_FOUND |
CACHE SIZE <n> | OK; sets the capacity (default 4) and evicts down to it. n below 1: ERR size must be at least 1; fewer than the pinned pages: ERR size smaller than pinned pages |
CACHE READ <id> | data, NOT_FOUND, or ERR all pages pinned (the miss is still counted) |
CACHE WRITE <id> <data> | OK or ERR all pages pinned |
CACHE PIN / UNPIN <id> | OK, ERR not cached, ERR not pinned |
CACHE FLUSH | OK flushed=<n> |
CACHE STATS | hits:<n>,misses:<n>,evictions:<n>,writebacks:<n> |
CACHE LIST | cached ids from most to least recently used; * marks dirty, @n a pin count; (empty) |
Every eviction counts one eviction; every page copied to disk (on eviction or flush) counts one writeback.
Common mistakes
- Not refreshing recency on a hit. The victim then depends on load order, not use.
- Dropping a dirty page without writing it.
DISK READafter the eviction must show the new data. - Evicting a pinned page, or counting an eviction when nothing could be evicted.
- Counting writes as hits. Only reads feed the hit counter.
Your exercise
The starter parses the commands and prints statistics and the list. You implement write_back, evict_one, make_room, read_page, write_page and flush_all. The mistake to avoid: choosing the victim by insertion order instead of last use.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…