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

LSM-tree storage engine

Module 2.3: Storage Engine Internals & Blob Systems Updated Sep 17, 2026
📄 Cheat Sheet
FAST database writes with LSM (Log-Structured Merge trees)
Ben Dicken FAST database writes with LSM (Log-Structured Merge trees) Watch on YouTube ↗

Ben Dicken (PlanetScale) builds up memtables, SSTables, levels and compaction with excellent interactive visuals and clear reasoning about costs.

0:00 / --:--

Under the Hood: Log-Structured Merge-Tree (LSM-Tree)

While Bitcask requires all keys to fit in memory, real-world big-data systems (Google Bigtable, Apache Cassandra, RocksDB, ScyllaDB) handle datasets where both keys and values vastly exceed RAM. The LSM-Tree organizes sorted data into a memory buffer and a hierarchy of disk-resident levels, enabling high write throughput, efficient range scans, and dense data compression.

The Multi-Tier LSM-Tree Architecture

flowchart TD Client["Application Write"] --> WAL["Write-Ahead Log (Disk: Append-Only)"] Client --> Memtable["Active Memtable: SkipList (RAM)"] Memtable -->|"Flushed when full (64MB)"| ImmMemtable["Immutable Memtable (RAM)"] ImmMemtable -->|"Background Flush"| L0["Level 0 SSTables (Disk: Keys Overlap)"] L0 -->|"Leveled Compaction"| L1["Level 1 SSTables (Disk: Non-Overlapping)"] L1 -->|"Leveled Compaction (10x Size)"| L2["Level 2 SSTables (Disk: Non-Overlapping)"]

Core Invariants of the LSM-Tree

  • Sorted Invariant: Data inside every Memtable and SSTable is strictly sorted by key. This enables binary search within blocks, efficient merge-sort during compaction, and fast range scans.
  • Immutability Invariant: Once an SSTable is written to disk, it is NEVER modified. Deletes and updates are handled by appending newer entries or tombstones.
  • Compaction-dependent layout: Level 0 files commonly overlap. Leveled compaction usually keeps files within each lower level non-overlapping, while size-tiered or universal strategies may retain overlapping sorted runs. The read path must follow the selected strategy's invariant.

Worked example: how many levels for 2 TB?

Using RocksDB-style defaults: 64 MB memtable, level 1 target 256 MB, and a size multiplier of 10 per level.

LevelTarget sizeCumulative
L1256 MB0.25 GB
L22.56 GB2.8 GB
L325.6 GB28 GB
L4256 GB284 GB
L52.56 TB2.8 TB

So 2 TB needs five levels below L0. Because each level is ten times the one above, about 90% of the data lives in the last level, which is why space amplification of leveled compaction is roughly 1.1x.

A point lookup, worst case

  1. Active memtable, then any immutable memtables: in RAM.
  2. Each L0 file, newest first (often up to about 4 before compaction is triggered).
  3. At most one file per level in L1 to L5, found by key range metadata.

That is up to about 9 candidate files. Bloom filters at 10 bits per key (about 1% false positives) mean a lookup for a key that exists costs roughly one data block read, plus a few percent extra from false positives, and a lookup for a missing key usually costs no data block reads at all.

Where LSM-trees are used

  • RocksDB: embedded engine inside MyRocks, CockroachDB (historically; now Pebble, a Go LSM), TiKV and Kafka Streams state stores.
  • Cassandra and ScyllaDB: per-table LSM storage with selectable compaction strategies.
  • Bigtable and HBase: tablets or regions made of SSTables or HFiles plus a memtable.
flowchart LR W["Write"] --> WAL["WAL append"] W --> MT["Memtable"] MT -->|"flush when full"| L0["L0 SSTables"] L0 -->|"compaction"| L1["L1 256 MB"] L1 -->|"compaction"| L2["L2 2.56 GB"] L2 -->|"compaction"| LN["L3 to L5"]
Check yourself Question 1 of 3
Why L0 is special recall
From: Core Invariants of the LSM-Tree

Why might a point lookup have to check every Level 0 file, while in leveled compaction it checks at most one file per lower level?

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

LSM-trees buffer writes in memory (Memtable) and persist them as immutable sorted files (SSTables).
Data is always kept sorted by key, enabling range queries and merge-sort compaction.
RocksDB and Cassandra use LSM-trees to absorb massive write workloads with minimal write latency.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue