ApiaryActive
Try: pause · settings · learn · wipe
← Community / Reading Room
LT
databases · 12 min read

LSM Trees in Modern NoSQL Stores

Traditional relational databases were built around the B‑Tree, a balanced‑search structure that optimizes random reads and writes by keeping data sorted on…

Log‑Structured Merge‑Trees (LSM Trees) have become the de‑facto storage engine for virtually every write‑heavy NoSQL system that powers today’s cloud‑native applications. From the billions of sensor events collected by an environmental monitoring platform to the trillions of user‑generated events that feed recommendation engines, LSM‑based stores turn a relentless stream of writes into a reliable, low‑latency service. This article unpacks how the LSM architecture works, why it excels at write‑intensive workloads, and how its design choices ripple through performance, cost, and even the sustainability of the data centers that host our digital ecosystems.


Introduction: Why Write‑Heavy Workloads Need a Different Tree

Traditional relational databases were built around the B‑Tree, a balanced‑search structure that optimizes random reads and writes by keeping data sorted on disk. For workloads that perform a few thousand writes per second, B‑Trees are fine. But modern applications—real‑time analytics, IoT telemetry, social‑media timelines, and large‑scale machine‑learning pipelines—often generate hundreds of thousands to millions of writes per second per node.

When each write forces a random disk seek, the I/O subsystem becomes a bottleneck. A 2023 benchmark from the Cloud Native Computing Foundation (CNCF) showed that a vanilla B‑Tree engine on a typical SSD tops out at ~120 k random writes per second before latency climbs past 10 ms, whereas an LSM‑based engine (RocksDB) on the same hardware sustained ~720 k writes per second with median latency under 2 ms. The difference isn’t just a matter of speed; it’s a matter of feasibility. Without a storage engine that can absorb write bursts without degrading latency, services either have to over‑provision hardware (inflating cost and energy use) or sacrifice data freshness—an unacceptable trade‑off for many real‑time systems.

The LSM Tree solves this by turning random writes into sequential appends, leveraging the fact that modern flash and NVMe drives excel at high‑throughput sequential I/O. It does so while preserving the ability to serve low‑latency reads, thanks to auxiliary structures like Bloom filters and block caches. The result is a storage engine that aligns with the economics of cloud pricing, the physics of modern storage media, and, indirectly, the energy efficiency goals of bee‑inspired, self‑governing AI agents that aim to keep our data ecosystems as sustainable as natural pollination networks.

In the sections that follow we will:

  • Walk through the anatomy of an LSM Tree, from write‑ahead log to compaction.
  • Examine the write and read paths with concrete numbers from production deployments.
  • Compare LSM Trees to B‑Trees and discuss when each shines.
  • Explore how major NoSQL platforms implement and tune LSM.
  • Look at emerging trends—time‑series, hybrid storage, and AI‑driven self‑optimization.
  • Close with a brief reflection on why these technical choices matter for the broader goals of conservation‑aware computing.

1. The Anatomy of an LSM Tree

An LSM Tree is not a single data structure; it is a pipeline of immutable sorted files (SSTables) and an in‑memory mutable component (the MemTable). The pipeline is orchestrated by three core mechanisms:

ComponentRoleTypical Size / Config
Write‑Ahead Log (WAL)Guarantees durability by sequentially appending every mutation before it reaches memory.64 MiB – 256 MiB segments; flushed to disk in < 10 ms.
MemTableIn‑memory sorted map (often a skip‑list or balanced tree) that buffers writes.64 MiB – 512 MiB; when full, it is frozen and scheduled for flush.
SSTable (Sorted String Table)Immutable, on‑disk sorted file containing a range of keys.64 MiB – 2 GiB per file; each level contains many SSTables.
Compaction EngineMerges overlapping SSTables to maintain read efficiency and control space amplification.Configurable strategies (leveled, tiered, universal).

The write path looks like this:

  1. Append to WAL – Guarantees crash recovery. This is a sequential write, usually < 0.5 ms on modern NVMe.
  2. Insert into MemTable – An in‑memory data structure that maintains sort order. The cost is O(log N) but N is bounded by the MemTable size, so latency stays in the low microseconds.
  3. MemTable Flush – When the MemTable reaches its size threshold, it is frozen, written to a new SSTable on disk (a flush), and the old WAL segment is discarded.
  4. Compaction – Periodically, overlapping SSTables are merged into larger, non‑overlapping files, cleaning up deleted/overwritten keys.

Because the only random I/O occurs during compaction (which can be throttled and scheduled during low‑load periods), the steady‑state write workload is dominated by sequential writes—exactly what flash media is optimized for.


2. Write Path Deep Dive: From Mutation to Disk

2.1 Write‑Ahead Logging (WAL)

The WAL is the first line of defense against data loss. In RocksDB, a typical configuration writes ~128 MiB per WAL segment. Each segment is flushed to the underlying block device using fdatasync to guarantee durability. Benchmarks on a 2 TB NVMe drive (Intel Optane P5800X) show average WAL latency of 0.34 ms per 4 KB write, well within the latency budget for high‑throughput services.

2.2 MemTable Insertion

Most LSM implementations use a skip‑list for the MemTable because it offers fast insertion, deletion, and iteration with a low memory footprint. A 2022 study on Cassandra’s MemTable (using a 256 MiB skip‑list) reported average insertion cost of 0.12 µs per entry, meaning a single node can ingest >8 million writes per second before the MemTable becomes a bottleneck. The real limit is the flush rate.

2.3 Flushing to SSTables

When the MemTable reaches its size limit, it is frozen and handed off to a background thread that writes it out as an SSTable. The process includes:

  • Sorting (already sorted in the MemTable) – no extra cost.
  • Compression – RocksDB’s default Snappy compresses typical JSON logs at a 2.1:1 ratio, adding ~0.8 µs per key-value pair.
  • Checksum calculation – 64‑bit CRC per 4 KB block, negligible overhead.

A single flush of a 256 MiB MemTable to a 2 GiB SSD takes ≈ 12 ms, yielding a write throughput of ~21 GB/s on the storage side (the SSD can sustain > 3 GB/s sequential write, so the bottleneck is CPU compression).

2.4 Write Amplification

Write amplification is the ratio of bytes written to storage vs bytes of user data. In a pure LSM pipeline with no compaction, each write incurs a 1× amplification (WAL + SSTable). However, compaction adds extra writes. In a leveled compaction strategy (used by default in Cassandra), typical amplification values are 2.5× – 3× for write‑heavy workloads. This means that for every 1 GB of user data, the system writes 2.5 GB–3 GB to disk over time.

RocksDB’s universal compaction can reduce write amplification to ~1.8× for append‑only workloads (e.g., time‑series), at the cost of higher read amplification. Understanding this trade‑off is critical for capacity planning and energy budgeting.


3. Read Path: Point Lookups, Range Scans, and Bloom Filters

LSM Trees excel at writes, but reads can be challenging because data is spread across multiple SSTable levels. Modern LSM implementations mitigate this with Bloom filters, block caches, and row caches.

3.1 Point Lookups

When a client requests key K, the engine:

  1. Checks the memtable (fast, in‑memory).
  2. Probes the block cache for a cached data block containing K.
  3. If not cached, queries each SSTable in order of newest to oldest, using the Bloom filter to skip files that definitely don’t contain K.

A typical RocksDB deployment with a 1 GiB block cache (≈ 5% of total data) achieves median point‑lookup latency of 0.8 ms for a 100 GB dataset with 10 M keys. The Bloom filter false‑positive rate is configurable; a 0.01% false‑positive rate reduces unnecessary disk reads to ≈ 1 per 10 000 lookups, saving ~0.6 ms per lookup.

3.2 Range Scans

Range scans (e.g., “all events between timestamps T1 and T2”) benefit from the sorted nature of SSTables. The engine performs a merge iterator across relevant files, reading sequential blocks. On an SSD, a full table scan of 200 GB completes in ≈ 5 seconds, corresponding to ~40 GB/s sequential read bandwidth—well within the drive’s capability.

In Apache HBase, which uses a tiered compaction strategy, range scans over a 1 TB table typically require reading ~1.4 TB of data due to overlapping SSTables, yielding a read amplification of 1.4×. This is acceptable for analytical workloads but can be tuned down by moving to leveled compaction at the cost of higher write amplification.

3.3 Caching Strategies

  • Block Cache – Stores decompressed data blocks (typically 4 KB–64 KB). Effective for hot keys; a 2 GiB cache can hold ~50 M keys for a 100 GB dataset.
  • Row Cache – Stores whole rows (or documents) in memory. Used sparingly because of high memory pressure; e.g., MongoDB’s WiredTiger enables a row cache of 256 MiB for write‑heavy workloads.
  • Cache‑aware Compaction – Some engines (e.g., ScyllaDB) schedule compactions to keep hot SSTables on faster media (NVMe) while cold ones migrate to slower HDDs.

4. Compaction Strategies: Keeping the Tree Clean

Compaction is the “maintenance” phase of an LSM Tree. It merges overlapping SSTables, discards obsolete versions, and re‑writes data in a way that reduces read amplification. The three most common strategies are:

StrategyHow It WorksWrite AmplificationRead AmplificationTypical Use‑Case
LeveledEach level holds SSTables of bounded size; files are merged into the next level when full.2.5× – 3×1.0× – 1.2×OLTP workloads (Cassandra, HBase default)
Tiered (Size‑Tiered)SSTables are grouped by size; when enough files of similar size exist, they are merged into a larger tier.1.8× – 2.2×1.5× – 2.0×Write‑once, read‑rare (time‑series, IoT)
UniversalCompacts based on overlapping key ranges regardless of size; aims to minimize write amplification for append‑only data.1.5× – 2.0×2.0× – 3.0×Log aggregation, event sourcing

4.1 Leveled Compaction Example (Cassandra)

Cassandra’s default LeveledCompactionStrategy (LCS) maintains L0 (unflushed MemTables), L1 (≈ 160 MiB), L2 (≈ 320 MiB), etc., doubling each level’s capacity. When L1 exceeds its threshold, overlapping SSTables are merged into L2. The process is I/O‑intensive, but Cassandra throttles compaction to a configurable max background I/O (default 100 MiB/s). In a 12‑node cluster handling 2 M writes/s, compaction consumes ≈ 30 % of total SSD bandwidth, leaving enough headroom for reads.

4.2 Tiered Compaction Example (RocksDB)

RocksDB’s SizeTieredCompactionStrategy (STCS) groups SSTables of similar size (e.g., 64 MiB). Once 4 such files exist, they are merged into a 256 MiB file. This reduces the number of files per level, decreasing manifest size (metadata) and improving write throughput. In a benchmark with 10 M writes/s on a 4‑node cluster, STCS achieved ~1.2 GB/s sustained write throughput with write amplification of 1.9×.

4.3 Universal Compaction Example (ScyllaDB)

ScyllaDB’s UniversalCompactionStrategy is optimized for log‑structured workloads. It merges files based on overlap ratio rather than size, which dramatically reduces the number of merge passes for monotonically increasing keys (e.g., timestamps). In a 2024 internal test on a 100 TB time‑series dataset, Scylla achieved write amplification of 1.6× and read amplification of 2.3×, while keeping CPU utilization under 30 %.


5. LSM in the Wild: How Major NoSQL Stores Implement It

5.1 Apache Cassandra

  • Engine: Uses LeveledCompactionStrategy by default, with an optional TimeWindowCompactionStrategy for time‑series tables.
  • Write Performance: In a 2023 benchmark (10 node cluster, 2 TB SSDs), Cassandra sustained ~1.3 M writes/s at median latency 1.4 ms.
  • Tuning knobs: memtable_total_space_in_mb, compaction_throughput_mb_per_sec, bloom_filter_fp_chance.

5.2 Apache HBase

  • Engine: Built on HFile, an LSM‑style file format; default compaction is tiered, with optional major compaction for full rewrites.
  • Write Performance: A 2022 Yahoo! Cloud Serving Benchmark (YCSB) run showed ~900 k writes/s on a 12‑node cluster, with average latency 2.1 ms.
  • Tuning knobs: hbase.regionserver.global.memstore.size, hbase.hstore.compaction.max, hbase.regionserver.compaction.throttle.

5.3 RocksDB (Embedded)

  • Engine: Offers Leveled, Size‑Tiered, Universal, and FIFO compaction strategies.
  • Write Performance: On a single‑node Intel Xeon (2.4 GHz) with a 4 TB NVMe, RocksDB achieved ~1.1 M writes/s at 0.9 ms latency using LeveledCompaction.
  • Tuning knobs: write_buffer_size, max_background_compactions, target_file_size_base.

5.4 LevelDB (Google)

  • Engine: Simpler than RocksDB; uses Size‑Tiered compaction only.
  • Write Performance: Benchmarks on a 2019‑era SSD show ~300 k writes/s—adequate for many mobile and embedded scenarios.
  • Tuning knobs: write_buffer_size, max_open_files.

5.5 Amazon DynamoDB (Managed)

  • Engine: Internally uses a proprietary LSM variant optimized for multi‑AZ replication and auto‑scaling.
  • Write Performance: DynamoDB’s on‑demand mode can scale to > 10 M writes/s across a table, with latency under 5 ms.
  • Tuning knobs: Provisioned throughput, auto‑scaling policies, partition key design (which determines LSM shard distribution).

5.6 MongoDB WiredTiger

  • Engine: Uses a B‑Tree for indexes but an LSM‑style log-structured storage for the data collection (WiredTiger’s file format).
  • Write Performance: In a 2023 performance test, a 3‑node replica set on NVMe achieved ~650 k writes/s with median latency 1.2 ms.
  • Tuning knobs: wiredTigerCacheSizeGB, wiredTigerCollectionBlockCompressor, indexBuildRetryAttempts.

6. Tuning LSM for Real‑World Workloads

Performance is not a static property; it hinges on configuration and workload characteristics.

ParameterEffectTypical RangeExample Impact
memtable_sizeLarger MemTables reduce flush frequency → fewer compactions.64 MiB – 1 GiBRaising from 256 MiB to 512 MiB cut flushes by 30 % and improved write throughput by ~8 % in a Cassandra benchmark.
max_background_compactionsControls parallel compaction threads.1 – 8 per nodeSetting to 4 on a 16‑core machine kept SSD utilization under 70 % while maintaining low write latency.
target_file_size_baseSize of SSTables at each level.64 MiB – 2 GiBLarger files reduce manifest overhead but increase compaction latency.
bloom_filter_fp_chanceFalse‑positive rate for Bloom filters.0.001 – 0.01Tightening from 0.01 to 0.001 reduced point‑lookup disk reads by 40 % on a 500 GB dataset.
compaction_throughput_mb_per_secI/O throttling for compaction.10 – 200 MiB/sLimiting to 50 MiB/s prevented compaction from starving foreground reads during peak traffic.

6.1 Write‑Heavy vs Read‑Heavy Trade‑offs

  • Write‑Heavy (e.g., log ingestion): Use Tiered or Universal compaction, larger MemTables, and relaxed Bloom filter settings to prioritize throughput.
  • Read‑Heavy (e.g., serving user profiles): Prefer Leveled compaction, smaller SSTables, tighter Bloom filters, and a larger block cache.

6.2 Space Amplification

Space amplification is the ratio of disk space used vs user data size. In a leveled LSM with a 10 GB dataset, you may see ~15 GB on disk due to overlapping SSTables and deleted tombstones. Enabling TTL‑based garbage collection (e.g., Cassandra’s gc_grace_seconds) can shrink this to ~12 GB after compaction.

6.3 Energy Considerations

Compaction is CPU‑ and I/O‑intensive. A study from the University of Zurich (2022) measured ~0.45 kWh per GB of data compacted on a typical 2‑socket server. By scheduling compaction during off‑peak hours (when renewable energy supply is higher) and using tiered compaction, operators reduced energy consumption by 12 % without affecting SLAs—a concrete example of how LSM tuning aligns with sustainability goals akin to the efficient foraging patterns of bee colonies.


7. LSM vs B‑Tree: A Quantitative Comparison

MetricLSM Tree (Leveled)B‑Tree (InnoDB)
Write Throughput (NVMe, 4 KB ops)720 k ops/s (median 2 ms)120 k ops/s (median 10 ms)
Write Amplification2.5× – 3×1× (no compaction)
Read Amplification (point)1.1× – 1.3× (with Bloom)1× (single tree)
Range Scan CostSequential reads across SSTables (1.2×–1.5×)Single tree traversal (1×)
Space Amplification1.2× – 1.5×1.0× – 1.1×
CPU OverheadHigher (compaction)Lower (no background merges)
Best ForWrite‑intensive, append‑only, time‑seriesTransactional OLTP with balanced reads/writes

The numbers illustrate that LSM Trees dominate when writes dominate, while B‑Trees still have a place in workloads with high update‑to‑insert ratios and strict low‑latency point reads.


8. Emerging Trends: Time‑Series, Hybrid Storage, and AI‑Driven Self‑Optimization

Frequently asked
What is LSM Trees in Modern NoSQL Stores about?
Traditional relational databases were built around the B‑Tree, a balanced‑search structure that optimizes random reads and writes by keeping data sorted on…
What should you know about introduction: Why Write‑Heavy Workloads Need a Different Tree?
Traditional relational databases were built around the B‑Tree, a balanced‑search structure that optimizes random reads and writes by keeping data sorted on disk. For workloads that perform a few thousand writes per second, B‑Trees are fine. But modern applications—real‑time analytics, IoT telemetry, social‑media…
What should you know about 1. The Anatomy of an LSM Tree?
An LSM Tree is not a single data structure; it is a pipeline of immutable sorted files (SSTables) and an in‑memory mutable component (the MemTable) . The pipeline is orchestrated by three core mechanisms:
What should you know about 2.1 Write‑Ahead Logging (WAL)?
The WAL is the first line of defense against data loss. In RocksDB, a typical configuration writes ~128 MiB per WAL segment. Each segment is flushed to the underlying block device using fdatasync to guarantee durability. Benchmarks on a 2 TB NVMe drive (Intel Optane P5800X) show average WAL latency of 0.34 ms per 4…
What should you know about 2.2 MemTable Insertion?
Most LSM implementations use a skip‑list for the MemTable because it offers fast insertion, deletion, and iteration with a low memory footprint. A 2022 study on Cassandra’s MemTable (using a 256 MiB skip‑list) reported average insertion cost of 0.12 µs per entry , meaning a single node can ingest >8 million writes…
References & sources
  1. Apiary Reading Room — Open, cited knowledge base — funded to keep bee & practical research free.
From the Apiary Reading Room. Opinion & editorial — not financial advice. We don't overclaim.
More from the Reading Room