Home › High-Level Design › Database Indexing: B-Trees and LSM Trees
D

Database Indexing: B-Trees and LSM Trees

The moment you say "we'll add an index" in a system design interview, a good interviewer leans in and wants to know what kind and why. That one answer tells them whether you understand how a database actually spends disk I/O, or whether you are just naming features you saw in a job posting. I have sat on both sides of that table, and the candidates who stand out can draw the write path on a whiteboard and explain, in plain terms, why Postgres and Cassandra behave so differently under a heavy write load.

High-Level DesignAdvanced11 min read

If you remember five things

The whole answer in one glance. Everything below defends these five calls.

  1. One fork decides everything. B-Trees overwrite in place; LSM Trees append and merge later. That single difference drives every trade-off that follows.
  2. B-Trees optimize reads. A point lookup is 3 or 4 page reads, mostly cached, with steady predictable latency. Writes pay with random I/O and page splits.
  3. LSM Trees optimize writes. Writes become sequential appends, so one node absorbs what would need five on a B-Tree. Reads and background compaction pay the bill.
  4. Workload shape picks the engine. Write-heavy ingest wants LSM; read-heavy transactional tables want a B-Tree. Say that unprompted and you look senior.
  5. Name the three amplifications. Write, read, and space amplification are the vocabulary that separates people who ran these systems from people who read a blog post.

1The core idea#

Start with what an index buys you, because the whole discussion hangs off this.

  • An index is a second copy of your data, organized so you can find rows without scanning everything.
  • Without one, finding a user by email in a 50 million row table means reading all 50 million rows: a full table scan touching maybe 400,000 pages at 16KB each, taking seconds.
  • With one, you read maybe 4 or 5 pages off disk and answer in single-digit milliseconds.
  • That is the whole pitch.

The interesting part is the data structure underneath. Almost every production database picks one of two families.

  • B-Trees (really B+Trees): the default for relational databases. Postgres, MySQL/InnoDB, SQL Server, Oracle. An update-in-place structure: to change a row you find the exact page it lives on and overwrite it.
  • LSM Trees (Log-Structured Merge Trees): the engine behind write-heavy stores. Cassandra, RocksDB, LevelDB, ScyllaDB, and the default backend in many time-series and key-value systems. An append-only structure: you never overwrite in place, you write new data and clean up later.

The mental model I use:

  • A B-Tree is a sorted filing cabinet. Every record has a home, and to change it you walk to that drawer and swap the sheet.
  • An LSM Tree is an accountant's ledger. You never erase a line, you append a new line that corrects the old one, and periodically you rewrite the book to throw out the stale entries.
The one sentence this whole guide defends

Overwrite-in-place versus append-and-merge. That single difference drives every trade-off below: read speed, write cost, space usage, latency shape, and failure mode. If you can only hold one idea, hold this one.

2How it works#

Two structures, two very different write paths. Walk each one top to bottom.

B+Tree#

A B+Tree is a balanced tree where each node is one disk page, usually 4KB to 16KB.

  • Internal nodes hold only keys and pointers to child nodes.
  • The actual row data (or pointers to it) lives only in the leaf nodes.
  • Leaves are linked left to right, so you can range-scan without climbing back up.

The tree stays shallow on purpose.

  • Fan-out is several hundred keys per node, so a tree holding billions of rows is only 3 or 4 levels deep.
  • A point lookup is 3 or 4 page reads, and the top levels sit in memory anyway, so in practice it is often 1 real disk read.

Writes are where it gets expensive.

  • To insert a key you walk down to the correct leaf and modify that page in place.
  • If the leaf is full, you split it into two and push a key up to the parent, which can cascade all the way to the root.
  • Every write is a random write to a specific location on disk.
  • On a spinning disk that is brutal: a 10ms seek per write caps you near 100 writes per second per disk.
  • Even on SSD it means read-modify-write at the page level, plus write amplification from partially-filled pages.

LSM Tree#

An LSM Tree flips the write path.

  • New writes go into an in-memory sorted structure called the memtable (often a skip list or balanced tree).
  • They also go into an append-only write-ahead log on disk for durability.
  • Both are fast: the log is a sequential append, the memtable is pure RAM.

When the memtable fills up:

  • It gets flushed to disk as an immutable sorted file called an SSTable. These files are never modified.
  • Over time you accumulate many of them, so a background process called compaction merges smaller SSTables into larger ones, dropping overwritten and deleted keys as it goes.

Deletes are the sneaky part.

  • You cannot erase from an immutable file, so a delete writes a marker called a tombstone.
  • The key looks deleted on read, and compaction physically removes it later.

Here is the LSM write and read path side by side.

flowchart TD A[Write request] --> B[Append to WAL] B --> C[Insert into Memtable] C --> D{Memtable full} D -->|Yes| E[Flush to SSTable L0] E --> F[Compaction merges levels] G[Read request] --> C G --> H[Check Bloom filters] H --> I[(SSTables on disk)] F --> I
The LSM paths crossing. The write side (left) is a straight line of sequential appends: WAL, memtable, flush, compaction. The read side (right) is the expensive one, probing the memtable then Bloom filters before it ever touches an SSTable on disk. Notice both paths converge on the same SSTables.

Reads are the cost center for LSM.

  • A key might be in the memtable, or in any of several SSTables.
  • A read checks multiple places, newest first, until it finds the key.
  • To avoid touching every file, each SSTable has a Bloom filter in memory that answers "this key is definitely not here" cheaply, so you only read files that might contain the key.
  • A Bloom filter tuned to a 1% false-positive rate costs about 10 bits per key, so an SSTable holding 10 million keys needs roughly 12MB of RAM. Cheap insurance against a disk read.

3Trade-offs#

The honest one-liner, worth memorizing word for word:

B-Trees optimize reads and pay on writes. LSM Trees optimize writes and pay on reads and on background work.

Be precise with the terms, because interviewers care about them.

  • Write amplification: bytes actually written to disk per byte of logical data. LSM compaction rewrites the same data several times as it moves down levels. B-Trees rewrite full pages even for a tiny change.
  • Read amplification: pages read per logical lookup. LSM can touch several SSTables plus Bloom filters. A B-Tree lookup is a predictable handful of pages.
  • Space amplification: disk used per byte of live data. B-Trees leave pages partially full (fragmentation). LSM temporarily keeps old versions and tombstones until compaction cleans up.
Dimension B+Tree LSM Tree
Write path Random, in-place, page splits Sequential append, batched flush
Point read Fast, predictable (3 to 4 pages) Slower, multiple files plus Bloom filters
Range scan Excellent (linked leaves) Good but merges across SSTables
Write throughput Lower, often low thousands per sec per node Very high, 100k-plus per sec per node
Write amplification Moderate, from page writes Higher, 10x-plus from compaction
Space usage Fragmentation overhead Old versions plus tombstones
Compression Harder (random pages) Easier (large sorted blocks)
Latency profile Steady Spiky if compaction falls behind

Two subtle points worth saying out loud in an interview.

  • Compaction competes with live traffic for disk and CPU. If writes arrive faster than compaction can keep up, SSTables pile up at level 0, reads slow down because each read checks more files, and you get a stall. The write-optimized store can fall over under exactly the write load it was supposed to handle well, if it is under-provisioned.
  • B-Tree writes are sequential in the WAL, but the actual data pages get written randomly on checkpoint. So "B-Trees do random writes" is true for the data files, not the log. Being precise here signals you have actually operated these systems.
From the interviewer's chair

When you drop "write amplification from compaction" at the right moment, I stop worrying about whether you memorized a diagram. The amplification vocabulary is my fastest filter: candidates who have run these systems reach for it naturally, and candidates who only read about them never do.

4Where it is used#

Match the engine to the workload. This is the judgment the question is really testing.

Reach for a B-Tree engine when:

  • Your workload is read-heavy or balanced, with lots of point lookups and range queries. An orders table, a user directory, anything behind OLTP.
  • You need strong, predictable read latency. A dashboard query that must return in 20ms every time is happier on a B-Tree.
  • You rely on relational features: joins, foreign keys, multi-column indexes, transactions. Postgres and InnoDB give you all of that on a B-Tree.

Reach for an LSM engine when:

  • Writes dominate. Event ingestion, metrics, logs, IoT sensor data, a feed of clicks. Cassandra and RocksDB-backed stores were built for this.
  • You are writing far more than you read back, or you read mostly recent data.
  • You want cheap sequential writes and good compression on commodity disks at scale.
Pause before you read on: a metrics pipeline takes 200,000 small writes per second. Put it on a single-node B-Tree. What breaks first, and roughly when?
Reveal the answer
Random I/O. Each write lands on a random leaf page, and even to change 100 bytes you rewrite the whole 16KB page. A decent NVMe SSD caps around 50,000 to 80,000 random page ops per second, so you cannot sustain 200k writes on one node. The write queue backs up almost immediately, and you are forced to shard across 4 or 5 nodes just to survive ingest. Reaching for LSM here, before being told, is the move that earns the room.

A worked example with real numbers#

Say you are building a time-series metrics pipeline taking 200,000 writes per second, each write a small 100-byte sample. That is 20MB of logical data per second, 1.7TB per day.

On a B-Tree:

  • Each write lands on a random leaf page.
  • Even if two samples share a 16KB page, you rewrite the whole 16KB page to change 100 bytes.
  • With a working set larger than RAM, each write turns into a page read plus a page write.
  • The random I/O pattern caps a decent NVMe SSD around 50,000 to 80,000 random page ops per second.
  • You cannot sustain 200k writes per second on one node: you either shard across 4 or 5 nodes just to survive ingest, or you fall behind and your write queue backs up.

On an LSM store:

  • Those same 200k writes per second are appends into the memtable plus one sequential WAL append.
  • A single sequential WAL stream handles 20MB per second trivially, well under what one SSD does sequentially (500MB per second-plus).
  • The memtable flushes to an SSTable every, say, 256MB, so roughly every 13 seconds you write one clean sequential file.
  • Compaction then rewrites that data a few more times as it moves down levels. If levels have a 10x size ratio and data passes through 4 levels, you write each byte about 4 to 5 times.
  • Write amplification is near 5x: you physically write around 100MB per second to absorb 20MB per second of logical data. Still comfortable sequential I/O, and it is why the LSM store absorbs the load on one or two nodes instead of five.
~5x
LSM write amplification on this pipelineEach logical byte gets physically written about five times as compaction moves it down four levels. That is the price LSM pays to turn 200k random writes into sequential appends, and it is why you watch SSD wear and compaction backlog, not just throughput.

Now flip the workload.

  • A product catalog is read 10,000 times for every write: 1 million reads per second, 100 writes per second.
  • On a B-Tree each read is a clean 3-page hop, top two levels cached, so about 1 real disk read at sub-millisecond latency, predictable p99.
  • On an LSM store that same read might check the memtable, miss, probe 4 SSTable Bloom filters, and read 1 or 2 of them off disk: 2x to 3x the read amplification for zero benefit, since writes were never the bottleneck.
  • The catalog belongs on a B-Tree.

The specific numbers move with hardware and tuning, but the shape is the point: the write-heavy ingest workload is a disaster on a B-Tree and easy on LSM, and the read-heavy catalog is the reverse.

The families converge at the edges.

  • InnoDB has a change buffer that batches secondary-index writes to soften the random-write cost.
  • RocksDB can be tuned toward read-optimized compaction.
  • They blur the line, but the defaults tell you what each was built for.

5Common pitfalls and gotchas#

These are the ones that bite in production, which is exactly why interviewers listen for them.

  • Treating "add an index" as free. Every secondary index is another B-Tree (or another set of SSTables) that must be updated on every write. I have seen a table with 11 indexes where inserts were 5x slower than they needed to be. Indexes cost write throughput and disk, not just the obvious storage.
  • Ignoring tombstones in LSM systems. Delete a lot of rows or write a lot of short-lived data in Cassandra, and tombstones accumulate. Reads that scan a range walk past thousands of dead markers, and queries slow to a crawl until compaction catches up. The classic outage: "we used a Cassandra table as a queue," which creates and deletes constantly and leaves a graveyard of tombstones in every partition.
  • Forgetting compaction is a tax you pay later. LSM write latency looks amazing in a 60-second benchmark. Run it for an hour under sustained load and watch compaction kick in. Good answers acknowledge that LSM trades steady latency for occasional spikes.
  • Write amplification on SSDs. Both structures write more than the logical data, which wears SSDs and eats IOPS. LSM compaction can rewrite data 10x or more as it cascades down levels. If someone asks "why is our SSD wearing out," this is a real answer.
  • Assuming range scans are equal. B+Tree leaves are linked, so a range scan is a sequential walk. LSM has to merge sorted runs across SSTables, which is more work and more unpredictable. For heavy range-query workloads, the B-Tree usually wins.
Red flag that fails you here

Naming a default engine and moving on. "I'll use an index to make it faster, probably a B-Tree since that's standard, and Postgres handles everything." No mention of the workload, what the index costs on writes, or when it hurts. That tells me you see the database as a black box and would happily ship the metrics pipeline onto the one engine that chokes on it.

6How it shows up in interviews#

Interviewers rarely ask "explain a B-Tree" directly.

  • It surfaces inside a bigger design: "Design a metrics system," "Design a chat app's message store," "Why did your database choke?"
  • The index discussion is how you justify your database choice.

What they probe:

  • Can you connect workload shape to storage engine? Write-heavy pushes you toward LSM, read-heavy toward B-Tree. Saying that unprompted is a strong signal.
  • Do you understand the three amplifications? Dropping "write amplification from compaction" at the right moment lands well.
  • Do you know the failure modes? Tombstone buildup, compaction stalls, random-write cost. These separate people who read a blog post from people who ran the thing.

Here is the same question answered two ways.

Names a tool
Weak answer
What it says
"I'll use an index, probably a B-Tree since that's standard, and Postgres handles everything."
Workload
Never mentioned.
Cost named
None. Index is treated as free.
What I hear
Database is a black box. Would ship the metrics pipeline onto the engine that chokes on it.
Picks a side
Strong answer
What it says
"Append-heavy event log at ~200k writes/sec, mostly recent reads, so an LSM store like Cassandra."
Workload
Tied to a number: one or two nodes absorb ingest where a B-Tree needs five and still thrashes.
Cost named
Read amplification and compaction load. Watches tombstone counts, keeps partitions bounded.
What I hear
Would flip to a B-Tree like Postgres for a read-heavy catalog. That judgment is the whole test.

Put the two next to each other and the difference is obvious. The weak answer names a tool. The strong answer picks a side, ties it to a number, names the cost it is accepting, says what it would monitor, and shows it would choose the other engine for a different workload. That last move, choosing differently given different load, is exactly the judgment the question is testing.

One more move that impresses: say what you would measure.

  • p99 read latency.
  • Compaction backlog.
  • SSD write amplification.
  • It shows you think past the whiteboard to running the system in production.

And get comfortable drawing the LSM write path from memory: WAL append, memtable insert, flush to SSTable, compaction. If you can draw it and explain why each step exists, you understand the structure better than most candidates who have only read about it.

Follow-ups the interviewer will ask (and your answers)
"Why is an LSM read slower than a B-Tree read?"
The key can live in the memtable or in any of several SSTables, so a read checks multiple places newest-first. Bloom filters cut this down by answering "definitely not here" cheaply, so you only read files that might hold the key, but you still pay read amplification. A B-Tree lookup is a predictable 3 or 4 page hop with the top levels cached, so often 1 real disk read.
"What exactly is a tombstone, and why does it hurt?"
You cannot erase from an immutable SSTable, so a delete writes a tombstone marker instead. The key reads as deleted immediately, and compaction physically removes it later. The hurt comes when they pile up: a range scan has to walk past thousands of dead markers until compaction catches up. Using a Cassandra table as a queue is the classic way to drown in them.
"Our SSDs are wearing out fast. Which structure and why?"
Likely LSM. Compaction rewrites the same data several times as it cascades down levels, so write amplification can hit 10x or more. Every one of those physical writes costs SSD endurance and IOPS. B-Trees amplify too (full-page rewrites for tiny changes) but usually less than deep LSM compaction. I'd check compaction settings and level ratios.
"You said B-Trees do random writes. Isn't the WAL sequential?"
Right, and that distinction matters. The write-ahead log is a sequential append. The random writes are on the data pages at checkpoint, when dirty pages flush to their specific locations on disk. So "B-Trees do random writes" is true for the data files, not the log.
"How do Bloom filters decide which SSTables to skip, and what do they cost?"
Each SSTable has an in-memory Bloom filter that can say "definitely not here" or "maybe here." Reads probe the filters first and only touch files that return maybe, which avoids most disk reads on misses. Cost is roughly 10 bits per key at a 1% false-positive rate, so an SSTable with 10 million keys needs about 12MB of RAM. Cheap insurance against a disk seek.

What you can now explain in the room

  • Why an index is a second sorted copy of your data, and why it turns a 400,000-page scan into 4 or 5 reads.
  • The one fork that drives everything: overwrite-in-place (B-Tree) versus append-and-merge (LSM).
  • The full LSM write path from memory: WAL append, memtable insert, flush to SSTable, compaction.
  • The three amplifications (write, read, space) and which structure pays each.
  • Why a 200k-writes-per-second pipeline is easy on LSM and a disaster on a single B-Tree node.
  • The failure modes interviewers listen for: tombstone buildup, compaction stalls, and SSD write amplification.

Next, try the System Design Question walkthrough on designing a time-series metrics store, where this B-Tree versus LSM decision is the first fork in the road.

Get a prep plan built around your Database Indexing: B-Trees and LSM Trees interview

Tell us the role and your interview date. We will send back a dated, day by day plan. Free.

Thanks. Check your inbox, your Database Indexing: B-Trees and LSM Trees prep plan is on the way.