Databases
Chapter 29: Database Backends (LevelDB, RocksDB, MDBX)
Section titled “Chapter 29: Database Backends (LevelDB, RocksDB, MDBX)”Overview
Section titled “Overview”Blockchain nodes use embedded key-value databases to store blocks, state, and receipts. The database engine you choose determines disk footprint, IOPS pressure, sync speed, and how much memory you need. This chapter compares the real engines used by Ethereum clients and what each tuning lever does.
29.1 The Storage Engines
Section titled “29.1 The Storage Engines”| Engine | Structure | Used by (Ethereum) | Characteristics |
|---|---|---|---|
| LevelDB | LSM tree | Geth (default), Reth (legacy) | Log-structured, compactions, write-optimized |
| Pebble | LSM tree (Go reimplementation of RocksDB) | Geth (optional via --db.engine pebble) | Less memory than LevelDB, faster range reads |
| RocksDB | LSM tree (C++, Facebook/FoundationDB) | Nethermind, Besu (default), many chains | Tunable, high write throughput |
| MDBX | B+ tree (memory-mapped) | Erigon, Reth | Read-optimized, memory-mapped, very low overhead |
LSM vs B+ Tree in one line
Section titled “LSM vs B+ Tree in one line”- LSM (LevelDB/RocksDB/Pebble) writes log-structured: batched in memory, flushed to immutable sorted files, then compacted in the background. Great write throughput; suffers write amplification (a byte of data may be written several times during compaction).
- B+ tree (MDBX) updates pages in place inside a memory-mapped file: excellent read performance and predictable IOPS; writes are more expensive per transaction but there are no background compaction storms.
╔══════════════════════════════════════════════════════════════╗║ LSM WRITE PATH MDBX WRITE PATH ║║ ║║ write ──▶ memtable ──▶ SST file ──▶ compaction ──▶ SST ║║ (batched, "in memory") (immutable) (merge, rewritten) ║║ ║║ MDBX: direct page write into the mmap'd B+ tree ║║ (transaction copies-on-write; readers never block) ║╚══════════════════════════════════════════════════════════════╝29.2 Engine Comparison
Section titled “29.2 Engine Comparison”| Property | LevelDB | Pebble | RocksDB | MDBX |
|---|---|---|---|---|
| Write throughput | Medium | High | High | Medium |
| Read throughput (point) | Medium | High | High | Very high |
| Range/iteration | Slow | High | High | Very high |
| Background compaction | Yes | Yes | Yes (tunable) | No |
| Memory overhead | Medium | Low | High | Low |
| Corruption recovery | Rerun compaction / resync | Same | Same | mmap — often recoverable |
Operational takeaway: for a busy RPC node, LSM engines burn IOPS on compaction after sync; MDBX shifts the load to reads. That is why Erigon/Reth (MDBX) feel faster on archive queries and why geth offers Pebble to cut memory and compaction cost.
29.3 Client Configuration
Section titled “29.3 Client Configuration”# Default is LevelDB; switch the engine at datadir creation time:geth --db.engine pebble --datadir /data/ethereum
# ⚠️ --db.engine is read once when the database is created;# changing it later requires a re-sync.The old make geth TAGS=rocksdb build path is gone — modern geth ships LevelDB + Pebble only.
Nethermind / Besu (RocksDB)
Section titled “Nethermind / Besu (RocksDB)”# Nethermind: --config mainnet uses RocksDB by defaultdotnet Nethermind.Runner.dll --config mainnet --JsonRpc.Enabled true
# Besu: Bonsai trie (reduce storage) with RocksDB backendbesu --data-storage-format=BONSAI --storage-engine=ROCKSDBNote Nethermind/Besu “Bonsai” is a state storage format (store only the current trie + diffs) — it is orthogonal to the underlying RocksDB vs LevelDB choice.
Erigon / Reth (MDBX)
Section titled “Erigon / Reth (MDBX)”# MDBX is built in; you mostly tune its file size / pagesize:erigon --db.size.limit 3tb --datadir /data/erigon29.4 What Actually Matters for Node Operators
Section titled “29.4 What Actually Matters for Node Operators”| Lever | What it does | Typical default |
|---|---|---|
--cache (geth) | Memory for DB/trie/snapshot caches | 1024 MB (barely enough) |
--db.engine (geth) | leveldb vs pebble | leveldb |
| Compaction settings | LSM only; tune with care | engine defaults |
| WAL | Write-ahead log: crash safety at cost of an extra write | on |
ulimit -n | FDs for thousands of SST files | fix to 65536 (ch47) |
| SSD/NVMe | IOPS ceiling for compactions | NVMe for production |
Signs Your DB Is the Bottleneck
Section titled “Signs Your DB Is the Bottleneck”iostat -x: %util saturating while CPU is idle → compaction storm or too-small cache.- Sync “stuck” while
topshows one core pegged → likely LSM compaction (MDBX nodes rarely show this). - RPC latency on
eth_getBalance/eth_getStorageAtcreeping as the trie grows → cache too small or engine read-bound.
29.5 Interview Talking Point
Section titled “29.5 Interview Talking Point”“Ethereum clients split into LSM engines (LevelDB/RocksDB/Pebble — write-optimized, background compaction) and B+ tree engines (MDBX — read-optimized, memory-mapped). For an RPC/archive workload I prefer MDBX-based clients (Erigon/Reth); for a system already on geth I enable Pebble and right-size
--cache. When the DB becomes the bottleneck I look at IOPS, compaction, and ulimit before throwing RAM at it.”
Summary
Section titled “Summary”- Database choice affects performance
- MDBX (Erigon/Reth) is fastest for reads; RocksDB/Pebble for writes
- Geth:
--db.engine pebble(set at creation time) - LSM engines need IOPS budget for compaction; watch iostat
- Storage format (Bonsai) is separate from storage engine (RocksDB)
Last Updated: 2026-09-28