My Account List Orders Book Page

The Log-Structured Merge Tree

Table of Contents

  • Introduction
  • Chapter 1 The Tyranny of In-Place Updates: B-Trees and the Disk Era
  • Chapter 2 The 1996 Breakthrough: O'Neil, Cheng, Gawlick, and O'Neil
  • Chapter 3 Ahead of Its Time: Why the Tech World Ignored the LSM Tree
  • Chapter 4 The Web Revolution: The Collapse of Relational Assumptions
  • Chapter 5 Google's Crisis: Crawling the Web at Unprecedented Scale
  • Chapter 6 Bigtable: The Resurrected Architecture
  • Chapter 7 Anatomy of an LSM Tree: MemTables, WALs, and Immutable SSTables
  • Chapter 8 Read Amplification: The Tax on Multi-Component Storage
  • Chapter 9 The Sifter: Bloom Filters and Point-Lookup Optimization
  • Chapter 10 The Art of Compaction: Size-Tiered vs. Leveled Strategies
  • Chapter 11 Write Amplification: The Silent Killer of Flash Memory
  • Chapter 12 Open Source Awakenings: The Birth of Apache Cassandra and HBase
  • Chapter 13 LevelDB: Jeff Dean and Sanjay Ghemawat's Elegant Blueprint
  • Chapter 14 Facebook and RocksDB: Tuning the Engine for the Social Era
  • Chapter 15 The Solid-State Revolution: How NAND Flash Crowned the LSM Tree
  • Chapter 16 Concurrency and the Log: Lock-Free MemTables and High-Throughput Ingestion
  • Chapter 17 NewSQL Arrives: CockroachDB, TiDB, and LSM-Backed Distributed Transactions
  • Chapter 18 Space Amplification: Navigating the Trade-Offs of Modern Compaction
  • Chapter 19 Tuning the Engine: The RUM Conjecture and LSM Mechanics
  • Chapter 20 Streaming and Real-Time Analytics: LSM Trees in Kafka and ClickHouse
  • Chapter 21 The Cloud-Native Shift: LSM Trees on Disaggregated Object Storage
  • Chapter 22 Hardware Symbiosis: Zoned Namespaces (ZNS) and Computational Storage
  • Chapter 23 The New Contenders: Learned Index Structures and Hybrid B-Trees
  • Chapter 24 Failure Modes in Production: Tail Latencies, Compaction Debt, and Crashes
  • Chapter 25 The Ubiquitous Tree: What the LSM Saga Teaches Us About Systems Design

Introduction

Every time you refresh your social media feed, complete an online transaction, or stream a piece of media, an invisible ballet of magnetic charges and electrical gates unfolds deep within the planetary-scale infrastructure of modern computing. Petabytes of raw telemetric records, high-velocity clickstreams, and financial ledgers pour into distributed clusters at rates that would have melted the mainframe data centers of the late twentieth century. For decades, the software engineering world believed it understood how to store, index, and retrieve this information. The gospel of database design was carved into stone during the 1970s, codified in the elegant mathematics of the relational model and physically manifested through the balanced tree—the venerable B-tree. The B-tree was built on a singular, intuitive premise: when data changes, you find its physical location on the disk and overwrite it in place.

That premise quietly broke the internet before the modern internet was even born. In-place updates, so natural to human reasoning, are anathema to physical hardware. On spinning magnetic platters, updating data in place meant paying an exorbitant tax in mechanical seek times, forcing physical actuator arms to sweep erratically back and forth across rotating discs. Later, with the advent of solid-state flash memory, in-place updates proved even more toxic, triggering catastrophic write cycles that wore out silicon gates and crippled system throughput via internal garbage collection. The digital economy was hurtling toward a physics problem: the world was producing write-heavy, high-velocity data at an exponential rate, but the data structures charged with holding that data were fundamentally incapable of absorbing the blow.

The salvation of modern data storage did not emerge from a hyper-funded corporate lab during the Silicon Valley cloud boom. It was published in an academic paper in 1996 by Patrick O’Neil, Edward Cheng, Dieter Gawlick, and Elizabeth O’Neil. Their proposal—the Log-Structured Merge-tree, or LSM tree—was an intellectual marvel that turned database dogma on its head. Instead of treating storage as an interactive ledger where values are rewritten in place, the LSM tree treated storage as an immutable, append-only historical record. All writes were welcomed instantly into volatile memory and then periodically flushed to disk in continuous, sequential, read-only runs. Mutations were never updated in place; they were superseded by newer writes, while background maintenance routines quietly reconciled and merged the sorted fragments. It was an architecture optimized for the fastest physical operation a drive can execute: uninterrupted sequential writing.

Yet, upon its release, the technology sector greeted this breakthrough with near-total apathy. The mid-1990s were the golden era of enterprise relational databases. Giants like Oracle, IBM, and Microsoft ruled corporate computing, catering to workloads characterized by modest transaction volumes and read-heavy queries. In an era where a database fit comfortably on a single server cabinet and write throughput was rarely the bottleneck, the LSM tree’s trade-offs—such as degraded point-lookup performance and the computational overhead of background data compaction—seemed like complicated solutions to a problem nobody had. The paper was cited, shelved, and largely forgotten by working practitioners, relegated to an obscure footnote in storage systems research.

The turn of the millennium changed everything. The explosion of the World Wide Web, followed swiftly by social networking, cloud computing, and real-time telemetry, unleashed an unprecedented flood of unformatted, write-intensive data. When Google set out to index the entire public web, its engineers discovered that traditional relational architectures crumbled under the sheer gravity of global-scale ingestion. In an act of profound engineering archeology, they dusted off the core principles of the LSM tree and forged Bigtable. The results sent shockwaves through the industry. When open-source implementations like Apache HBase and Cassandra appeared, followed by the crystalline engineering of LevelDB and Facebook’s production-hardened RocksDB, the forgotten algorithm transformed into an unstoppable engineering juggernaut. Today, whether you are querying a distributed NewSQL engine like CockroachDB, running real-time analytics in ClickHouse, or persisting messages in streaming backbones, you are almost certainly leaning on the quiet, hyper-optimized mechanics of an LSM tree.

This book is the definitive biography of that data structure and an engineering manual for the modern storage era. It is written for software architects, systems engineers, database administrators, and curious technologists who refuse to treat their storage engines as black boxes. Across these pages, we will trace the journey of the LSM tree from its theoretical origins in the early 1990s through its current status as the foundational engine of Big Data. We will dissect its internal anatomy—from the delicate balance of write-ahead logs and lock-free MemTables to the probabilistic magic of Bloom filters and the intricate mathematics of leveled and tiered compaction. More importantly, we will confront the harsh realities of production engineering: the brutal trade-offs between read amplification, write amplification, and space amplification, and the nightmare of compaction debt and tail latencies that haunt on-call engineers.

The story of the Log-Structured Merge-tree is more than a technical breakdown of pointers, blocks, and SSTables. It is a masterclass in the evolution of software architecture—a vivid reminder that the best ideas in computer science are rarely those that conform to the prevailing wisdom of their day. Instead, transformative software architectures emerge when engineers dare to align algorithmic design with the unyielding realities of physical hardware. Step inside the engine room of the modern data stack, and discover how a discarded academic concept was resurrected to power the fastest databases on Earth.


CHAPTER ONE: The Tyranny of In-Place Updates: B-Trees and the Disk Era

To understand why the computing world eventually had to tear down and rebuild its data storage foundations, one must first appreciate the physical reality of a hard disk drive spinning inside a metal chassis in the 1970s and 1980s. Long before software engineers were insulated by cloud abstractions, container runtimes, and virtualized block storage, a database was a program locked in a physical struggle with mechanical hardware. The machines of that era were massive, noisy cabinets harboring aluminum platters coated with microscopic magnetic domains, spun continuously by electric motors at speeds that made the entire server room hum. Suspended mere micro-inches above those whirring surfaces floated an actuator arm carrying an inductive read/write head.

Retrieving or recording a byte of information was not an abstract mathematical act. It was an exercise in ballistics. If an application requested a record residing on track forty-two, sector seven, but the drive head was idling over track eighty, the controller had to push current into a voice coil, swing the physical metal arm across the physical air gap, bring it to a dead stop over the target cylinder, wait for the mechanical vibrations to settle—an agony known as seek time—and then wait again while the disk rotated the desired sector directly underneath the sensor. That final delay, rotational latency, meant that even if the drive head arrived at the correct track instantaneously, it might spend half a rotation waiting for the magnetic patch to glide into position. In the temporal reference frame of a modern CPU running instructions in mere nanoseconds, a ten-millisecond disk seek was an eternity; it was the computational equivalent of asking a desk clerk for a folder and waiting six months for them to walk across a continent to fetch it.

Because mechanical latency dominated all other performance metrics by orders of magnitude, early system designers faced a singular imperative: every single interaction with persistent storage had to count. You could not afford to read or write loose, scattered bytes. Instead, operating systems and storage engines chopped up disk space into uniform chunks known as blocks or pages, typically four to eight kilobytes in size. If you needed to change a single customer’s telephone number within an eight-kilobyte page, the machine did not attempt to surgically alter those fifty bits on the magnetic substrate. It pulled the entire eight-kilobyte page off the platter into random-access memory, updated the characters in silicon, and then pushed that entire eight-kilobyte page back down to the exact same physical spot on the spinning platter.

This mechanic became known as the in-place update. It was an intuitive, tidy way of conceptualizing state. In-place updates mapped cleanly to human intuitions about physical space. If you maintain a physical ledger book and someone changes their address, you turn to page seventy-four, locate the person’s line, erase the obsolete street name, and write the new street name directly in the empty white space. The ledger does not expand. The page count does not swell. Page seventy-four remains page seventy-four, and anyone looking for that person in the future needs only look at that single, unchanging location. For nearly three decades, virtually every database architecture on Earth was constructed around this premise.

At the epicenter of this paradigm stood the B-tree. Conceived in 1970 by Rudolf Bayer and Edward M. McCreight while working at Boeing Scientific Research Laboratories, the B-tree was arguably the most successful data structure in the history of commercial computing. Bayer and McCreight set out to solve a specific problem: how do you maintain a sorted, indexed collection of millions of records when the index itself is far too gigantic to fit into volatile memory? Traditional binary search trees, such as AVL trees or red-black trees, were disastrous on external storage. In a binary tree, every single node contains at most two children. Traversing an index of a million keys requires following up to twenty pointers down through the tree. If every pointer dereference forces a distinct mechanical disk head seek, retrieving a single record could consume two hundred milliseconds of wall-clock time—a rate of roughly five queries per second per drive.

Bayer and McCreight’s solution was to widen the tree aggressively. Instead of a node holding two pointers and one key, a B-tree node was engineered to correspond precisely to the size of an entire disk page. Inside an eight-kilobyte page, a storage engine could comfortably pack hundreds of keys and child pointers. A B-tree with a fan-out factor of five hundred needs only three or four tiers of nodes to index billions of records. The root page of the tree, sitting at the very top, is tiny enough to remain permanently pinned in volatile RAM. When an engine searches for a key, it inspects the root page in memory, determines which child page holds the relevant range, performs one disk seek to pull that second-tier page into memory, inspects its keys, and makes another seek to the third tier. In just three or four physical seeks, the system can locate any arbitrary record in a multi-terabyte dataset.

The variant that quickly became standard across relational systems was the B+ tree. In this refinement, internal nodes contain strictly guide keys and pointers, acting solely as a road map, while the actual leaf nodes at the bottom of the tree contain the row data or pointers to heap files. Furthermore, all the leaf nodes are linked together sequentially in a doubly-linked list. This layout meant that a B+ tree was not only peerless at individual point lookups, but it was also brilliant at range scans. If an application wanted all customer records where the registration date fell between March 1 and March 31, it traversed the tree once to find the leaf page holding March 1, and then simply followed the sequential leaf pointers sideways across the bottom layer, reading contiguous pages off the platter in a smooth, continuous mechanical sweep.

By the late 1970s and through the 1980s, the B+ tree had become the unquestioned foundation of the database universe. When IBM built System R and popularized the SQL language, it leaned heavily on B-trees. When Larry Ellison founded Oracle and conquered the enterprise computing market, the internal storage engine was governed by B-trees. Informix, Sybase, Microsoft SQL Server, and later open-source systems like PostgreSQL and MySQL’s InnoDB engine all paid homage to the same architecture. The academic community produced hundreds of papers optimizing the model: concurrent page-latching algorithms, write-ahead logging protocols to ensure crash recovery, prefix compression to squeeze more keys into each internal page, and sophisticated buffer pool management strategies to keep the hottest pages cached in memory.

For decades, this machinery operated with astounding success because the dominant workloads of commercial computing matched its characteristics with eerie perfection. In banking, insurance, inventory management, and airline reservations, the dominant transaction patterns were balanced and interactive. A teller at a bank branch looked up an account balance, a process that required a fast read. A human being then paused for several seconds to look at the screen, confirm an identity, and decide to withdraw fifty dollars. The teller submitted the transaction, which triggered an update to a single balance column, followed by an insert into a transaction log. The ratio of reads to writes was often ten to one, or even a hundred to one.

When writes did arrive, they were sparse, trickle updates. The B-tree handled them with consummate grace. The engine would traverse the index to find the exact leaf page hosting the account, load that page into the system’s volatile buffer pool, update the balance in place within the page’s byte structure, mark the page as dirty in memory, and append a compact operational record to an append-only transaction log—the write-ahead log, or WAL—to guarantee durability in the event of an unpredicted power loss. Later, during quiet periods, an asynchronous background thread would take the dirty page from memory and flush it back to its original slot on the disk.

The system was orderly, transactional, and robust. It provided absolute consistency and ACID guarantees. As long as transactions trickled in at human speeds—tens or hundreds of requests per second, mediated by human cashiers, travel agents, and inventory clerks typing on green-screen terminals—the mechanical overhead of the in-place update model remained well below the saturation ceiling of the physical hardware.

The trouble with in-place updates, however, was that beneath this veneer of mathematical tidiness lay a dark physical reality. The entire design took for granted that disks were fundamentally fast enough to absorb the write traffic assigned to them. But the moment write traffic ceased to be a polite trickle and transformed into a firehose, the physics of the magnetic platter asserted itself with unforgiving brutality.

To see the fault line, consider what actually happens inside a B-tree when you execute an arbitrary insertion or update. Unlike a append-only log, where every new piece of information is simply tacked onto the end of an ever-growing file, an insertion into a B-tree cannot just go anywhere. A B-tree is an ordered data structure. The new key must be placed into a specific leaf page to maintain sorted order. If you are inserting keys generated by a monotonically increasing sequence—such as auto-incrementing integer IDs—the inserts will neatly hit the rightmost leaf page of the tree, filling it up sequentially. But the moment your workload inserts non-sequential keys—such as UUIDs, alphanumeric customer names, email addresses, or transaction hashes—the inserts are distributed randomly across the entire breadth of the tree’s leaf layer.

Suppose a database index contains ten million pages distributed across a physical drive. A stream of writes featuring random keys will touch page twenty-four, then page nine hundred thousand, then page seventy-two, then page four million. For the database to modify these pages, each page must be fetched from disk if it does not already reside in the buffer pool. If the working set of the index exceeds the size of volatile RAM—which, in the era of expensive memory chips, it almost always did—the cache hit ratio plummeted. Every single insert suddenly required a mechanical seek to read the page into memory, followed shortly thereafter by another mechanical seek to write the modified page back to disk.

This phenomenon is the scourge known to systems engineers as random write amplification. The application might only be attempting to update an eight-byte integer representing a user's loyalty points, but the storage engine is forced to locate, read, and rewrite a full eight thousand bytes of data on a distant physical cylinder. When an application attempts to push three hundred random writes per second against a standard 7,200 RPM enterprise SCSI hard drive, it runs squarely into a hard physical wall. A 7,200 RPM drive completes one revolution in roughly 8.3 milliseconds. Between the rotational delay and the physical swing of the voice coil arm, a high-end enterprise drive of the late 1980s and 1990s could execute somewhere between one hundred and two hundred random I/O operations per second (IOPS).

If a workload attempted to execute five hundred random writes per second against an index that did not fit in memory, the server did not simply slow down; it ground to an absolute halt. The operating system’s I/O queue filled to capacity, processes backed up into uninterruptible sleep states, and the actuator arm inside the drive engaged in a violent, continuous physical spasm known as disk thrashing. The disk heads darted furiously across the platters, chattering loudly in the chassis as they attempted to satisfy hundreds of competing demands to write tiny eight-kilobyte pages to completely disparate sectors of the disk. The physical throughput of the drive plummeted. A drive capable of transferring fifty megabytes of sequential data per second was suddenly reduced to moving less than one megabyte per second because ninety-nine percent of its operational time was spent swinging the head through thin air rather than reading or writing bits.

The mechanics grew even more pathological when a leaf page in a B-tree ran out of room. Because B-tree nodes are bound to a strict physical page size, you cannot simply shove an extra key into a page that is already packed to capacity. When an insertion lands on a full page, the engine must execute a page split. It allocates a brand-new, empty page from the operating system, copies half of the keys from the overflowing page into the new page, inserts the incoming key into its proper sorted location, and then updates the parent node in the tier above to add a new guide key and a pointer to the newly allocated page.

A page split was an architectural small-scale disaster for high-velocity writing. A single insert operation, which the client application assumed was an instantaneous step, suddenly triggered multiple random writes: writing the old page, writing the new page, and writing the modified parent page. If the parent page was also full, the split propagated upward to the grandparent page, occasionally cascading all the way to the root of the tree. During this structural modification, the affected pages had to be protected by exclusive locks or heavy latches, preventing other threads from reading or writing to that entire branch of the hierarchy. If a high-volume stream of inserts caused cascading page splits across multiple threads, the storage engine suffered massive internal concurrency bottlenecks.

Database engineers did not simply surrender to this mechanical reality; they devised brilliant, complex systems to stave off the catastrophe. The most critical defense was the database buffer pool and its associated flushing algorithms. The buffer pool was a large segment of volatile memory carved out to act as an intermediary between the execution engine and the raw disk. When a page was modified, it was retained in the buffer pool as a dirty page for as long as possible. The hope was that subsequent updates might land on the same page while it was still in memory, thereby amortizing the cost of the eventual physical disk write across multiple logical transactions.

To prevent sudden crashes from wiping out modifications resting exclusively in volatile memory, the system relied on the Write-Ahead Log. Before any dirty page could be rewritten to disk, the change had to be recorded in an append-only log file located on a separate physical device, if possible. The WAL was the one place where relational databases embraced sequential writes: changes were written to the tail of the log in a continuous, unbroken stream. Sequential writing was fast; the disk head stayed parked on a single track, writing block after block without moving the arm, operating at the maximum bus bandwidth of the drive. The engine could return a success response to the client as soon as the WAL reached persistent storage, safe in the knowledge that if the server lost power a millisecond later, the recovery process could replay the WAL and reconstruct the state of the dirty pages.

Yet the WAL was merely an insurance policy, not an escape from the eventual bill. The dirty pages lingering in memory still had to be flushed to their permanent home in the B-tree at some point. If the buffer pool became full of dirty pages, it could no longer service incoming read requests without evicting pages. To manage this, databases employed sophisticated background processes with names like checkpoint daemons or page cleaners. These threads scanned the buffer pool, grouped dirty pages together by cylinder address, sorted them to minimize drive head travel, and flushed them in batches.

In high-concurrency systems, however, checkpointing created notorious performance cliffs. An enterprise database might run smoothly for twenty minutes, processing transactions with sub-millisecond latencies, until the volume of dirty pages crossed a critical watermark. Suddenly, the checkpoint daemon would awaken in a panic, seizing the I/O bus to flush thousands of accumulated pages down to the B-trees on disk. The drive queues exploded, application queries were starved of disk bandwidth, and user transactions spiked to multi-second latencies until the checkpoint cleared. This roller-coaster dynamic—smooth operation punctuated by violent latency spikes—was an accepted, documented reality of late-twentieth-century enterprise computing. Administrators learned to live with it, scheduling batch updates in the dead of night and constantly tuning esoteric parameters governing buffer flush intervals, dirty page percentages, and fill-factors.

Another palliative strategy was the deliberate de-densification of the B-tree through the use of fill factors. When creating an index, a database administrator could instruct the engine to pack leaf pages to only fifty or seventy percent of their capacity, leaving intentional blank space in every node. When new random inserts arrived, they could theoretically slip into the pre-allocated empty spaces without immediately triggering an expensive page split. But this was a Faustian bargain. Leaving pages half-empty dramatically inflated the physical footprint of the entire database on disk. A database that should have consumed fifty gigabytes ballooned to one hundred gigabytes. Because the database was physically larger, fewer total pages could fit into the operating system’s RAM cache at any given time. As a direct consequence, the cache hit ratio plummeted, forcing the engine to make even more mechanical disk reads to satisfy queries. In trying to solve the write amplification problem, administrators inadvertently degraded their read performance and accelerated their hardware consumption.

Hardware vendors, recognizing the immense pain their enterprise customers were experiencing, responded not by changing the data structures, but by throwing expensive, specialized hardware at the problem. Enter the era of the enterprise storage array: massive, multi-million-dollar refrigeration-sized cabinets manufactured by companies like EMC, IBM, and NetApp. These appliances, marketed under the banner of Storage Area Networks (SAN) and Redundant Arrays of Independent Disks (RAID), were essentially giant computers whose sole function was to act as an elaborate shock absorber for in-place updates.

Inside a high-end enterprise SAN sat hundreds of physical spinning hard drives coupled with massive batteries, redundant power supplies, and gigabytes of battery-backed non-volatile RAM (NVRAM). When a database engine wrote a dirty B-tree page to the SAN, the storage array intercepted the write and swallowed it directly into its high-speed NVRAM cache, immediately telling the database that the write was safely on disk. The database resumed operations, oblivious to the fact that the page was still floating in volatile memory. The SAN then spent the next several minutes using its own dedicated processors to sort the thousands of cached writes, organizing them by physical cylinder, and lazily writing them down to the underlying disk platters using complex elevator algorithms to minimize actuator arm movements.

For Fortune 500 corporations, financial institutions, and global telecom providers, this hardware sledgehammer was an acceptable solution. If your relational database was buckling under the write strain of processing millions of credit card transactions a day, you simply cut a seven-figure check to a storage vendor, installed a monolithic SAN, and used battery-backed caches to hide the structural shortcomings of the B-tree.

But this solution contained the seeds of its own obsolescence. It was an extraordinarily expensive band-aid that depended entirely on a specific, centralized economic model. The enterprise world was content because its datasets, by modern standards, were tiny. In the late 1980s and early 1990s, a database holding one hundred gigabytes of data was considered colossal—an enterprise-grade leviathan requiring specialized tape backups and a dedicated team of systems administrators. At that scale, if you threw enough physical disks into a RAID-10 array, the aggregate spindle count could supply just enough random IOPS to keep the B-trees from collapsing under their own weight.

Beneath the surface of this uneasy truce, however, the fundamental physics remained unyielding. The B-tree, by its very nature, demanded that the physical location of a record be tied to its logical key. To mutate a key, you had to mutate a specific byte range within a specific page on a specific drive. The B-tree was an architecture designed around the absolute authority of the read path. It was an engine optimized above all else to ensure that when an analytical or point lookup query arrived, the machine could pluck the answer from persistent storage with the absolute minimum number of page accesses. Bayer and McCreight had achieved this goal brilliantly. For read queries, the B-tree was a masterpiece of mechanical conservation.

The price paid for this read perfection was an absolute, non-negotiable tax on the write path. Every write was condemned to a cycle of search, load, modify, and overwrite. As long as computing remained centered on traditional corporate record-keeping—where data was read far more often than it was altered, and where transactions were counted in dozens per second—the tax was bearable. The industry paid it in hardware costs, in complex caching logic, and in the restless thrashing of mechanical drive heads.

What virtually no one foresaw in the early 1990s was the impending democratization of high-velocity data generation. The world was on the cusp of an era where computers would no longer simply record the occasional financial transfer or inventory deduction initiated by a human clerk. In the wings waited the automated world: clickstreams, automated server logs, machine-generated sensor telemetry, continuous telecommunication records, and eventually, the crawling and indexing of the entire global web.

This impending wave of data would invert the historical ratios of computing. Instead of ten reads for every write, systems would soon be confronted with workloads featuring thousands of writes for every single read. In this coming environment, data would arrive not as polite, ordered queries, but as a relentless, non-stop physical torrent. Against such a deluge, the mechanical reality of the in-place update—the requirement that an actuator arm swing across space to overwrite an existing page simply because an integer changed—was not merely inefficient. It was an insurmountable physical dead end.


This is a sample preview. The complete book contains 27 sections.