SD System Design In Depth
0%
lesson system-designstorage-engines

Memtable, WAL, and SSTable

Module 2.3: Storage Engine Internals & Blob Systems Updated Sep 17, 2026
📄 Cheat Sheet
LSM Tree + SSTable Database Indexes | Systems Design Interview: 0 to 1 with Google Software Engineer
Jordan has no life LSM Tree + SSTable Database Indexes | Systems Design Interview: 0 to 1 with Google Software Engineer Watch on YouTube ↗

Jordan walks the write path through WAL, memtable and SSTable, plus sparse indexes and compaction, at exactly the depth this unit targets.

0:00 / --:--

The Triad of LSM Engines: Write Path Deep Dive

Every write operation in an LSM-tree database coordinates between three distinct components to provide durability (once the WAL append is synced), high concurrency, and rapid searchability.

The Write Path Coordination

flowchart LR Req["Mutation: Put(Key, Val)"] --> WAL["1. Append to WAL (Disk: O_APPEND)"] Req --> Memtable["2. Insert into SkipList (DRAM)"] Memtable --> LimitCheck{"Memtable Full? (> 64MB)"} LimitCheck -->|"No"| Ack["Return Success to Client"] LimitCheck -->|"Yes"| Freeze["Freeze as Immutable Memtable"] Freeze --> FlushWorker["Background Flush Thread"] FlushWorker --> SSTable["Write New SSTable to L0 (Disk)"] SSTable --> Truncate["Truncate Obsolete WAL Log"]

Under the Hood: Component Mechanics

1. The Write-Ahead Log (WAL)

Because the in-memory Memtable is volatile, a server power failure would lose uncommitted writes. Before inserting into the Memtable, the engine appends the operation to the WAL on disk. Because the WAL is append-only, disk throughput reaches hundreds of megabytes per second. Upon recovery, the engine replays the WAL to reconstruct the Memtable.

2. The Memtable (Concurrent SkipList)

The Memtable is an in-memory sorted collection. Balanced trees such as red-black trees need rebalancing that is hard to make lock-free, whereas concurrent skiplists can be updated with atomic compare-and-swap (CAS) on forward pointers. Multiple application threads can write concurrently without mutex contention while maintaining sorted key order.

3. The Sorted String Table (SSTable)

When the Memtable exceeds its threshold (typically 64MB or 128MB), it transitions to an immutable state and a fresh Memtable is allocated. A background thread iterates over the sorted Memtable sequentially, writing it to disk as an SSTable. An SSTable consists of data blocks (typically about 4KB before compression), an index block mapping keys to block offsets, and a Bloom filter.

Worked example: one write, one flush, one crash

  1. put("k17", v) is appended to 000123.log and inserted into the active 64 MB memtable (a skiplist).
  2. After roughly 64 MB of writes (about 60,000 records of 1 KB), the memtable becomes immutable and a new memtable and a new log file start.
  3. A background thread iterates the immutable memtable in key order and writes 000124.sst: data blocks of about 4 KB (before compression), an index block with one entry per data block, a filter block (Bloom filter), and a fixed-size footer pointing to the index.
  4. After the SST is synced and recorded in the MANIFEST, 000123.log can be deleted.
  5. If the process crashes before step 4, recovery reads the MANIFEST to learn the live SSTs and replays any log files not yet covered, rebuilding the memtable.

Durability knobs

ModeWhat is lost on power failureThroughputExample
Sync every writeNothing acknowledgedLowest, bounded by flush latencyRocksDB sync=true
Group or batch syncNothing acknowledged; writes wait for the next group flushHighCassandra batch or group commitlog modes
Periodic syncUp to the sync intervalHighestCassandra default periodic, 10 s
No WALEntire memtableHighestBulk loads that can be redone

Why a skiplist

A skiplist keeps keys sorted with expected O(log n) inserts and is easy to make safe for concurrent readers with a single writer (LevelDB) or concurrent writers using compare-and-swap on forward pointers (RocksDB's concurrent memtable writes). Sorted order is what makes the flush a single sequential pass.

Failure modes

  • Write stalls: if flushes cannot keep up, immutable memtables pile up and the engine blocks writers.
  • Slow recovery: many large WAL files mean long replay after a crash.
  • Torn WAL tail: per-record checksums let recovery stop at the last complete record.
Check yourself Question 1 of 3
When a WAL can go recall
From: Worked example: one write, one flush, one crash

A memtable backed by log file 000123.log has been frozen and is being flushed to 000124.sst. At what point can 000123.log safely be deleted?

Storage Write Amplifier

LSM-Tree

Memtable Append -> SSTable Flush

Total I/O: 0 ops

B-Tree

In-place Page Update -> Dirty Flush

Total I/O: 0 ops

Key Takeaways

WAL provides durability (once appends are synced) via fast sequential appends, while Memtable provides fast concurrent in-memory sorting.
SkipLists are preferred over balanced trees for Memtables because CAS pointers allow lock-free concurrent writes.
Flushing Memtables sequentially writes immutable SSTables containing data blocks, sparse indexes, and Bloom filters.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue