Concept lesson · Foundations
Storage engines and data models
Start here
Definition
A data model defines how an application represents and addresses records. A storage engine implements how those records and indexes are organized in memory and on disk, updated, and recovered after failure.
Why it matters: The same logical write can create very different disk, memory, and background-maintenance work depending on the engine.
B+ trees update indexed pages. LSM engines append and merge immutable sorted runs; read and write amplification trade off.
Read the diagram step by step
- A B+ tree routes through separator keys to a leaf page. WAL requires recovery records before dirty data pages reach durable storage; this example also flushes the commit record before acknowledging a durable transaction.
- An LSM write records a WAL entry and updates a memtable, which later flushes to a sorted run.
- Reads merge visible versions from memory and runs. Compaction rewrites runs and removes obsolete entries when safe.
- Keep a tombstone until older data cannot resurrect, accounting for replicas and retained snapshots as well as local files.
Worked example
Message 42 changes from “Train at five” to “Train at six.” A B-tree updates relevant pages; an LSM can retain the old file and place version 2 in a memory table and later a new sorted file.
Key takeaways
- SQL, documents, and key-value interfaces are a different choice from B-tree or LSM storage.
- A write-ahead log (WAL) supports crash recovery from durable records; surviving loss of the log’s storage still requires replication or backups.
- Measure read, write, and space amplification during steady-state maintenance.
You will learn to
- Separate an application’s logical data model from the engine’s physical layout.
- Trace B-tree and LSM reads, writes, recovery, and deletion using actual keys.
- Explain read, write, and space amplification before choosing an engine.
Practice in this chapter
8 interview questions with model answers and follow-ups.
Go to interview practiceUseful foundations: Database indexes: B-trees, composite keys and query access · Databases, data models, and ACID transactions
Workload and timing examples are interview assumptions.
Dotted concept links open the relevant explanation in a new tab.
01Data model versus storage engine
Choose the logical key independently of the storage engine. A message record can be (room_id, sequence, author_id, body, version). Fetching the latest fifty messages in one room favors an ordered key beginning with room and sequence. For example, a request updates message 42 in room R7 from “Train at five” to “Train at six” and deletes message 8, whose old value is “Hello.”
One machine can store this correctly. It becomes slow when the active data no longer fits memory or disk work exceeds capacity. Before adding shards, understand which physical work each logical write creates. A write-heavy service can saturate its storage while the incoming request count appears modest.
02B-trees and B+ trees: ordered page lookup
A B-tree keeps keys ordered in a branching tree of pages. A page is a block that the engine reads or writes as a unit. Internal pages guide a search toward a child; leaf pages contain index entries, with the exact record layout depending on the engine. A B+ tree keeps record-bearing entries in leaves and supports walking adjacent leaves for ranges.
This schematic B+ tree stores record entries at the leaves. Internal separator keys guide the search; all leaves are the same distance from the root.
Remember: Seek through the hierarchy; scan across leaves.
Read the diagram
- The root separator 40 chooses one child page.
- At the internal page containing 60, key 50 selects the child below 60.
- The leaf containing 40 and 50 holds the matching key and record reference.
- All leaves have equal depth. Linked leaves support ranges in this B+ tree example.
Imagine the root’s separators for R7 are message 20 and message 50. Looking up 42 follows the middle child to a leaf containing 21, 35, 42 and 49. If that index entry points to a separately stored row, fetching the message body is additional work. A latest-fifty query can seek near the end of R7’s range and walk backward through ordered entries.
Changing 42 updates the relevant data and index structures rather than scanning every message. A full leaf may split, requiring parent changes. Cached upper pages reduce physical reads, but cache misses, page splits, transaction versions, and recovery logging still matter. “Logarithmic lookup” describes growth; it does not specify a fixed number of disk operations for every product.
03Write-ahead logging: recovery and acknowledgment
A write-ahead log, or WAL, makes the recovery records for a change durable before the corresponding changed data pages are written to durable storage. That is the write-ahead ordering rule: the log reaches durable storage first. After a crash, the engine can reconstruct committed state from durable records and its persisted files. The exact protocol varies; a log is a recovery mechanism, not automatically an application event stream.
Read the top row before the crash, then the bottom row during recovery. This example assumes synchronous local durability.
Remember: Log first; recovery can redo a page update later.
Read the diagram
- Recover x = 9 from the persisted log when the data page still says x = 8.
- The WAL record becomes durable before the commit reply.
- A crash occurs before the changed page is flushed.
- Recovery replays the durable log to reconstruct the required state.
Try from memoryWhat makes x = 9 recoverable when the page still contains 8?
The recovery record is durable before success. Recovery can redo the committed change from WAL.
Assume the database acknowledges an edit only after its required recovery and commit records are durable under the configured local storage policy. At time 0 it logs message 42 version 2. At time 1 it acknowledges the edit. If the process crashes before the ordinary data page is flushed, recovery can replay the relevant durable information. If the service instead acknowledges only an in-memory buffer, the same crash may lose the edit.
State which failures each storage stage can survive:
| Acknowledged bytes have reached | Failure they can survive under the stated assumptions | Remaining risk |
|---|---|---|
| Only a process buffer | No guarantee after that process dies | Buffered records may vanish |
| Operating-system cache | A process crash if the OS and its buffered bytes survive | Reboot or power loss can lose unsynchronized data |
| Synchronized recovery log on durable media | Process or machine restart with that media intact | Device loss or a storage stack that violates synchronization |
| Required remote durable replicas too | The failures covered by the replica placement and commit protocol | Correlated loss beyond that failure model |
Synchronization asks the storage stack to persist the necessary bytes; it does not make one local device indestructible. Group commit lets several transactions share one synchronization operation. This can improve throughput, but a transaction may wait for the group before receiving its acknowledgment.
- 1 → 2log under chosen policyUpdate message 42 → Durable WAL record
- 2 → 3applyDurable WAL record → Memory table: 42 v2
- 3 → 4flushMemory table: 42 v2 → Flush sorted file
- 4 → 6mergeFlush sorted file → Compaction retains needed versions
- 5 → 6compare versionsOlder file: 42 v1 → Compaction retains needed versions
04LSM trees: memory tables and immutable sorted files
A log-structured merge tree, abbreviated LSM, accumulates updates in a memory table and writes sorted immutable files as buffers fill. The WAL protects updates that have not yet become durable table files under the chosen configuration. Immutable means a later edit is stored as another version rather than rewriting that old file in place.
Two sorted files contain different versions of A. This example assumes no snapshot needs the old version.
Remember: Merge keys; resolve versions; retain anything still required.
Read the diagram
- Follow the two copies of A into one merged output.
- New file: A = 9 and C = 3. Old file: A = 8 and B = 2.
- Merged output: A = 9, B = 2, C = 3. A = 8 is no longer required here.
Try from memoryWhy does A = 8 disappear, but B = 2 remain?
A has a newer value, 9, and no required snapshot needs 8 in this example. B has no replacement, so it remains.
| Location after the update and deletion | Entries for R7 | Meaning |
|---|---|---|
| Older sorted file F1 | 8 v1 = Hello; 42 v1 = Train at five | Earlier stored values |
| Newer memory table | 8 v2 = deletion marker; 42 v2 = Train at six | Latest changes |
| New file F2 after flush | Same newer entries, sorted by key | Memory can be reclaimed when safe |
A read of message 42 must select the newest visible version according to the engine’s ordering and snapshot rules. It cannot stop at v1 merely because F1 was convenient to open. A read of message 8 encounters a deletion marker, often called a tombstone, which suppresses its older value. Range reads merge ordered streams from relevant files. They are supported, but their cost depends on how many streams and obsolete versions must be considered.
05Compaction, tombstones, and amplification
| Cost | Plain definition | Example consequence |
|---|---|---|
| Read amplification | Extra data or storage operations needed for one logical read | Several candidate files for message 42 |
| Write amplification | Physical bytes written per logical byte ingested | Rewriting retained records during compaction |
| Space amplification | Physical storage relative to live logical data | Old versions and temporary compaction outputs |
Assume an illustrative workload ingests 100 MB/s and the measured total local write amplification, including the log in this measurement, is 8. The device must sustain about 800 MB/s of writes, before adding other workloads or safety margin. This is arithmetic from assumed inputs, not a hardware guarantee. Compaction also consumes read bandwidth and CPU. Deferring it forever makes later reads and space usage worse.
Two common compaction policies move that cost differently. Leveled compaction limits overlap within deeper levels, usually reducing read and space amplification but rewriting overlapping data. Tiered compaction accumulates several sorted runs before merging them, often reducing write amplification while increasing read sources and temporary space. These are tendencies, not universal benchmark results: key order, skew, overwrite rate and tuning matter.
06Storage-engine comparison and row versus column layouts
Two different physical choices are being compared. B-trees and LSM trees organize key lookup and update work. Row-oriented and column-oriented layouts determine whether fields of one record or values of one field are stored together. These choices can be combined; select them from whether the workload fetches individual messages, scans room ranges, or analyzes a few fields across many messages.
| Physical approach | How it handles work | Useful starting point | Cost to measure |
|---|---|---|---|
| B-tree/B+ tree | Seek through ordered pages; update affected structures | Point lookups and ordered ranges | Cache misses, page changes/splits, logging, and version cleanup |
| LSM tree | Buffer updates; flush and merge immutable sorted files | Sustained writes with an ordered-key design | Compaction, multiple read sources, obsolete versions, and temporary space |
| Row-oriented layout | Keep one record's fields together | Fetch a message and its metadata | Scans of a few columns may read unnecessary fields |
| Column-oriented analytical layout | Group values by column | Scan selected fields across many records | Reconstructing or updating individual records can cost more |
Green cells are totals. The first layout groups each person’s fields; the second groups each field’s values.
Remember: Whole row: fields together. Column scan: one field together.
Read the diagram
- Locate the same three totals in row-oriented and column-oriented storage.
- The example records are (1, Ada, 20), (2, Bo, 30), (3, Cy, 40).
- A column layout stores 20, 30 and 40 together; a row layout places each with its other fields.
Try from memoryWhich layout groups the bytes needed for SUM(total)?
The column layout groups 20, 30 and 40. The row layout stores each total beside that record’s other fields.
For an assumed room-history workload dominated by appends and bounded room-range reads, I would evaluate an LSM-backed ordered store. The key (room, sequence) makes the common range explicit. I would benchmark it against an indexed relational design before assuming its extra operational complexity is worthwhile. The choice depends on latency targets, transactional requirements, updates, retention and operating experience.
A key-value API does not remove the need to design keys. Hashing every entire message key across shards scatters a room’s range; partitioning by room preserves locality but creates a hot partition for a huge room. Time buckets or subpartitions can bound growth at the cost of merging reads. Physical engine selection does not solve those ownership decisions.
Row-oriented storage places a record’s fields together, useful when fetching a message. Column-oriented analytical storage groups values by column, useful when scanning a few fields across many records. A wide-column database’s data model is not synonymous with a columnar analytics layout. Ask which query the layout accelerates rather than matching names.
A practical baseline is PostgreSQL with an ordered B-tree index for transactional room history. Evaluate RocksDB when the application needs an embedded ordered key-value engine and can own the surrounding service protocol; RocksDB alone is not a replicated database service. Its write options distinguish asynchronous WAL writes from synchronized writes. If an acknowledged edit must survive machine restart, verify that WAL is enabled and the required synchronization policy is applied rather than assuming the default write call provides it.
07Storage failure, recovery, and benchmarking
After a crash, check that every acknowledged change covered by the durability policy survived, including the new value of message 42 and the deletion of message 8. A deleted message disappearing from ordinary reads does not prove its bytes vanished from snapshots, old files, replicas or backups; physical erasure follows a separate retention and cleanup policy.
In an interview I would say: “The key supports room-history reads. An LSM may suit frequent appends, but edits and deletion markers leave versions that reads and compaction must resolve. I will state which failures saved messages survive, budget the extra reads, writes and disk space, and test range reads while background maintenance runs.”
Practise the interview questions
Say your answer aloud before opening the model answer. Then answer the follow-up and compare the reasoning.
What is a storage engine, and how is it different from a data model?
Reveal a model answer
The data model describes records and access semantics, such as messages keyed by room and sequence. The engine organizes their bytes and indexes and performs updates and recovery. B-trees and LSM trees are engine techniques; relational tables and documents are logical models. Choosing SQL does not by itself select a B-tree or define its disk cost.
Interviewer follow-up
Can a document database use a B-tree?
Reveal the follow-up answer
Yes. A document representation does not force one physical engine. I compare the actual implementation’s transactions, indexes, durability, and maintenance work for the required queries.
What the answer must demonstrate: Distinguish the logical interface from physical organization.
A B-tree has separators 20 and 50; its middle leaf contains 21, 35, 42, 49. Explain lookup for key 42.
Reveal a model answer
“The root separators guide me to the relevant leaf range, where I find 42’s index entry. Depending on the layout, that entry contains the needed data or points to a separate row. Cached pages can avoid disk reads.”
Interviewer follow-up
Why not say it always takes three I/Os?
Reveal the follow-up answer
“The tree’s height, cached pages, record layout and overflow data all affect physical work.”
What the answer must demonstrate: Distinguish logical search steps from physical I/O.
An update is acknowledged before its changed data page reaches disk. Under what WAL policy can it survive a process crash?
Reveal a model answer
It can survive when the required recovery records, including the commit decision, were made durable before acknowledgment and recovery correctly replays them. Log-before-data ordering alone does not prove commit-before-ack durability. I must verify the configured synchronization policy and failure model.
Interviewer follow-up
What if the disk is destroyed?
Reveal the follow-up answer
“Then a local WAL alone is insufficient; the replica or backup durability policy determines what survives.”
What the answer must demonstrate: Name the acknowledgment boundary and failure model.
Why can an LSM contain two values for message 42?
Reveal a model answer
“The old sorted file cannot be changed. An edit first enters a newer memory table and later another file. Reads use the engine’s sequence and snapshot rules to choose the right version. Compaction removes old versions once they are no longer needed.”
Interviewer follow-up
Can a read return the first copy it finds?
Reveal the follow-up answer
“Only if the search protocol proves it is the correct visible version. Arbitrary file traversal is not sufficient.”
What the answer must demonstrate: Explain version visibility, not just file count.
An LSM contains a tombstone for key 8 and older files may contain key 8’s value. When may the tombstone be removed?
Reveal a model answer
“Only when the engine can prove older values cannot reappear for supported reads and no required snapshot needs that history. Removing the marker merely because it is old can expose an older stored copy.”
Interviewer follow-up
Does removing it erase backups too?
What the answer must demonstrate: Logical deletion, compaction and physical erasure differ.
What does write amplification of 8 mean at 100 MB/s ingestion?
Reveal a model answer
“With a measurement that includes all the relevant local writes, it implies roughly 800 MB/s of device writes. I would also budget compaction reads, CPU, replication and headroom, and verify the figure under a steady workload.”
Interviewer follow-up
Can you compare two quoted amplification numbers directly?
Reveal the follow-up answer
“Only if their numerator, denominator, workload and inclusion of logs or replication match.”
What the answer must demonstrate: Define the measurement before multiplying it.
Why does an ordered engine not automatically give fast room history?
Reveal a model answer
“The logical key and partitioning still matter. If each full message key is independently hashed to a different shard, a room query fans out. Keeping room and sequence together gives locality but may create a hot room partition.”
Interviewer follow-up
What does bucketing change?
Reveal the follow-up answer
“It bounds one partition’s size or traffic, while making history retrieval merge results from several buckets.”
What the answer must demonstrate: Connect query shape to both ordering and partitioning.
How would you test the engine choice?
Reveal a model answer
“I would load representative data, sustain ingestion until compaction reaches normal behavior, and measure tail latency for latest-fifty reads, edits, deletions and recovery. An empty database’s short insert burst hides the deferred maintenance cost.”
Interviewer follow-up
What failure would make you reconsider the choice?
Reveal the follow-up answer
“A persistent backlog of compaction work or range-read latency percentiles exceeding the target would prompt layout and resource changes, or a simpler engine better suited to the actual workload.”
What the answer must demonstrate: Evaluate steady-state operation, not only peak foreground throughput.
Blank-page exercise · 16 minutes
Build the answer yourself
Design storage for room R7 history: append messages, fetch the latest fifty, edit one message and delete another. Draw where two versions and a deletion marker exist before and after compaction.
- Specify the logical record, partitioning boundary and ordered key.
- Show which records are durably stored before the write is acknowledged and how recovery uses them after a crash.
- Explain how a read selects the newest visible value across files.
- Budget compaction, snapshots and recovery instead of counting only live payload bytes.
Check that each component and design decision follows from your requirements and workload.
Recall the key ideas
Answer from memory before opening each card. Explain why the choice works and what it costs. Revisit missed cards tomorrow.
Storage engines and data modelsWhat is the difference between model and engine?Recall first, then reveal
The model defines records and access semantics; the engine defines physical pages, files, logs and update behavior.
Meaning above; mechanics below
Return to lessonStorage engines and data modelsWhy does a tombstone exist?Recall first, then reveal
It records deletion so an older value in another file does not become visible again.
A delete must outlive the old copy
Return to lessonStorage engines and data modelsIs a buffered append free of later work?Recall first, then reveal
No. Flushing and compaction convert fast foreground writes into later I/O, CPU and space costs.
Append now, organize later
Return to lessonStorage engines and data modelsDoes WAL imply survival of disk loss?Recall first, then reveal
No. Local recovery logging and off-machine redundancy cover different failures.
Log repairs a crash; copies cover loss
Return to lessonFinal revision
Summary and interview notes
Choose a logical key that serves the query, then choose an engine and durability policy that can maintain it within the workload budget. B-trees and LSM trees move work differently; neither removes the need to account for versions, background maintenance, recovery and partitioning.
Remember these points
- Logical SQL/document/key-value models are distinct from physical B-tree, LSM, row and column layouts.
- Persist recovery information in the log before the corresponding data pages reach disk. To promise crash recovery, also persist the required commit information before reporting success.
- An LSM read chooses the visible version using values and deletion markers in memory and relevant sorted files.
- Compaction exchanges foreground speed for later reads, rewrites and temporary space; benchmark steady state.
- A tombstone may be dropped only when old values cannot reappear for supported reads and retained snapshots.
Interview tips
- Trace one acknowledged update through log, memory, file and recovery before comparing throughput.
- Multiply measured write amplification by ingress bytes and include compaction reads, replication and headroom.
- Name the query and key layout before saying B-tree or LSM; test hot partitions separately from engine speed.
Important qualifications
- RocksDB is an embedded engine; replication, failover and application transaction ownership need a surrounding system.
- Logical deletion is not proof of physical erasure from old files, snapshots or backups.
Technical references
- RocksDB OverviewVerified implementation reference for memory tables, sorted files, point reads, and range traversal.
- PostgreSQL: Write-Ahead LoggingOfficial explanation of log-before-data ordering, acknowledgment, and crash recovery; the lesson separately identifies LSM-specific memory-table flushing.
- RocksDB: CompactionVerified reference for sorted-run organization and amplification tradeoffs.
- PostgreSQL: B-Tree IndexesOfficial reference for ordered B-tree indexing. The tiny page and throughput examples are illustrative, not engine benchmarks.
- RocksDB: Basic OperationsChecked synchronous/non-synchronous writes, OS-buffer boundary and disableWAL behavior; durability remains configuration dependent.
Practice marks stay in this browser.