Why architecture matters here

The LSM-vs-B-tree distinction matters because it's the fundamental storage-engine choice that determines a database's performance profile, and understanding it explains why different databases suit different workloads. When you choose a database (or understand one's behavior), the storage engine's structure is a first-order factor: a B-tree engine (PostgreSQL, MySQL) will have fast reads and handle read-heavy workloads well but face write throughput limits; an LSM engine (Cassandra, RocksDB-based) will handle high write throughput well but may have read latency variance. This isn't a minor implementation detail — it's why Cassandra excels at write-heavy workloads (LSM's write optimization) while PostgreSQL excels at complex read-heavy queries (B-tree's read optimization). Understanding the storage engine explains the database's strengths and weaknesses, and choosing a database means (partly) choosing a storage-engine family matched to your workload's read/write balance. For anyone selecting databases or reasoning about their performance, this distinction is foundational.

The write-optimization insight of LSM trees is the architectural crux, and it's why LSM trees emerged for write-heavy modern workloads. B-trees update in place: a write finds the right page and modifies it — which means random I/O (the pages to update are scattered) and write amplification (a small update may re-write a full page, and page splits cascade). For write-heavy workloads (high insert/update rates — time series, logs, high-throughput applications), this in-place random-I/O writing is a bottleneck. LSM trees invert it: writes just append (to an in-memory memtable, then flushed sequentially to disk as sorted files) — sequential I/O (fast, especially on spinning disks but also SSDs), no in-place modification (no random writes, no immediate page splits). The cost is deferred to reads (data spread across files, requiring merging) and to background compaction (merging files, which re-writes data — write amplification, but done in the background, batched, sequentially). This trade — fast sequential writes now, deferred read cost and background compaction — is what makes LSM trees excel at write-heavy workloads, and it's why they power the write-optimized databases (Cassandra, RocksDB) that emerged for high-write-throughput modern applications. Understanding that LSM trees trade read complexity and background compaction for fast sequential writes explains their write-optimization and their read/compaction tradeoffs.

And the amplification framework is the analytical tool for understanding the tradeoffs concretely. Every storage engine faces three amplifications, and the LSM-vs-B-tree choice trades them: write amplification (B-trees: full-page writes and splits; LSM: compaction re-writing data — both amplify writes, but differently, and LSM's is tunable via compaction strategy), read amplification (B-trees: tree traversal, low and predictable; LSM: checking multiple levels/files, higher, mitigated by bloom filters), space amplification (B-trees: page fragmentation and unused space; LSM: overwritten and deleted data held until compaction — space used by obsolete data). The three amplifications interact and trade off (LSM compaction strategies trade write amplification against read and space amplification — leveled compaction lowers space and read amplification but raises write amplification; size-tiered lowers write amplification but raises space and read). Understanding a storage engine through its amplifications — and understanding that tuning (especially LSM compaction) trades them — is the analytical framework for reasoning about storage performance and tuning, and it's why 'which amplification matters most for my workload' is the key question in storage engine selection and tuning.

Advertisement

The architecture: every piece explained

Top row: the structures and paths. B-tree: a balanced tree of pages, data sorted, updated in place — reads traverse from root to leaf (logarithmic, direct), writes find and modify pages. LSM tree: writes append to an in-memory memtable, flushed to immutable sorted files (SSTables) on disk, organized in levels, merged by compaction — write-optimized append-and-merge. Write path: B-tree writes modify pages in place (random I/O, page-granular); LSM writes append to the memtable (in-memory, fast) then flush sequentially (sequential I/O) — the fundamental write difference (in-place random vs append sequential). Read path: B-tree reads traverse the tree directly to the data (one path, predictable); LSM reads may check the memtable and multiple SSTables across levels (the data could be in any, and newer versions shadow older) — merging results, higher read complexity.

Middle row: the amplifications and compaction. Write amplification: B-trees write full pages and cascade splits (a small write amplified to page writes); LSM compaction re-writes data merging files (data written multiple times as it moves through levels) — both amplify writes. Read amplification: LSM's key weakness — a read may check multiple levels (each a potential disk read) to find the data or confirm its absence — versus B-tree's single tree traversal (low read amplification). Space amplification: B-trees fragment (partially-full pages, unused space); LSM trees hold obsolete data (overwritten values and deletes-as-tombstones held until compaction removes them) — both use extra space. Compaction: LSM's background process — merging SSTables (combining, removing obsolete/deleted data, reorganizing levels) — essential to LSM (bounding read and space amplification by merging files) but costly (the background write amplification), with strategies (leveled, size-tiered) trading the amplifications.

Bottom rows: mitigation and choice. Bloom filters and caching: LSM's read amplification mitigation — bloom filters (per SSTable, quickly determining if a key is definitely absent, avoiding checking that file — crucial for reads that would otherwise check every level) and caching (block cache, holding hot data) — making LSM reads much faster than the naive multi-level check. Which for which workload: B-trees for read-heavy and read-latency-sensitive workloads (fast predictable reads, transactional workloads); LSM trees for write-heavy workloads (high write throughput — time series, logs, high-ingest) — the workload-based choice (though modern engines blur it, and both are tunable). The ops strip: workload matching (choosing the engine family matched to the read/write balance and latency needs), tuning (especially LSM compaction strategy — leveled vs size-tiered, trading the amplifications for the workload), and amplification tradeoffs (understanding which amplification matters most for the workload and tuning for it — write amplification for write-heavy, read for read-heavy, space for cost-sensitive).

LSM trees vs B-trees — the two storage engine familieswrite-optimized vs read-optimized, and the tradeoffsB-treein-place, balanced, read-fastLSM treeappend, merge, write-fastWrite pathin-place vs sequentialRead pathdirect vs merge levelsWrite amplificationB-tree pages vs LSM compactionRead amplificationLSM level checksSpace amplificationfragmentation vs overwritesCompactionLSM's background mergeBloom filters + cachingLSM read mitigationWhich for which workloadthe choiceOps — workload matching + tuning + amplification tradeoffsamplifyamplifyamplifycompactmitigatechooseoperateoperateoperate
LSM vs B-tree: B-trees update in place (read-optimized), LSM trees append and merge (write-optimized) — trading write, read, and space amplification differently.
Advertisement

End-to-end flow

Trace the two engines on a write-heavy workload — say, ingesting time-series data (high insert rate). On a B-tree engine: each insert finds its position in the tree and modifies the page in place — random I/O (the positions scatter across the tree), and write amplification (each insert may re-write a page, and inserts cause page splits cascading up the tree). At high insert rates, this random-I/O in-place writing becomes the bottleneck (the disk can't keep up with the random writes and page modifications). On an LSM engine: each insert appends to the in-memory memtable (fast, in-memory), and when the memtable fills, it flushes sequentially to a sorted SSTable (sequential I/O — fast) — the high insert rate is handled by fast sequential writes, no random I/O, no immediate page splits. The LSM engine handles the write-heavy workload far better (sequential appends vs random in-place writes) — demonstrating LSM's write optimization. The cost is deferred: reads will need to check multiple SSTables (mitigated by bloom filters), and background compaction will merge the SSTables (write amplification, but background and sequential).

The read-amplification and mitigation vignettes show LSM's tradeoff and its management. A read case: a point lookup on the LSM engine — the data could be in the memtable or any SSTable across the levels. Naively, this would check every level (read amplification — many disk reads to find the data or confirm absence). But bloom filters mitigate it: each SSTable's bloom filter is checked first (a fast in-memory check saying 'this key is definitely not in this SSTable' for most SSTables), so the read skips the SSTables that definitely don't have the key and only reads the one(s) that might — turning a multi-level scan into typically one or two SSTable reads. Bloom filters are essential to LSM read performance (without them, reads would be slow from checking every level; with them, reads are fast). The block cache further helps (hot data cached). So LSM reads, though structurally requiring multi-level checks, are fast in practice via bloom filters and caching — the mitigation that makes LSM's write optimization not come at an unacceptable read cost.

The compaction-tuning and choice vignettes complete it. A compaction case: the LSM engine's compaction strategy trades the amplifications — the team's write-heavy workload uses size-tiered compaction (lower write amplification — less re-writing — suiting the high write rate, at the cost of higher space and read amplification), while a more read-heavy LSM deployment uses leveled compaction (lower read and space amplification — better reads, less space — at the cost of higher write amplification). The compaction strategy is tuned to the workload's amplification priorities. A choice case: the team chooses engines by workload — the time-series ingest (write-heavy) uses an LSM engine (Cassandra/RocksDB-based), while a transactional application with complex read-heavy queries uses a B-tree engine (PostgreSQL) — matching the engine family to the read/write balance. The consolidated discipline the team documents: understand the storage engine family (B-tree read-optimized in-place, LSM write-optimized append-merge) as a first-order performance factor, choose the family matched to the workload (LSM for write-heavy, B-tree for read-heavy/latency-sensitive), reason through the three amplifications (write/read/space — and which matters for the workload), leverage LSM read mitigation (bloom filters and caching — essential), tune LSM compaction strategy trading the amplifications for the workload, and understand the tradeoffs rather than assuming one is universally better — because the LSM-vs-B-tree choice is the fundamental storage-engine decision, trading write, read, and space amplification differently, and matching the engine to the workload's read/write balance and amplification priorities is what makes the storage perform.