Back to Rocksdb

Performance

docs/components/write_flow/10_performance.md

11.8.16.2 KB
Original Source

Performance

Files: db/db_impl/db_impl_write.cc, db/write_thread.h, db/write_controller.h, db/memtable.h, db/log_writer.h

Hot Path Optimizations

WriteThread

  • Lock-free enqueue: LinkOne() uses a CAS loop on newest_writer_, avoiding mutex acquisition for every write
  • Adaptive wait: Three-phase spin-yield-block strategy minimizes latency for short waits while falling back to condvar for longer waits
  • Group commit: Leader batches multiple writers' WAL writes into a single fsync, amortizing sync cost across the group
  • O(sqrt(n)) parallel launch: Large parallel memtable groups use stride leaders to reduce serial wake-up overhead

MemTable

  • Lock-free skiplist: Default MemTableRep uses a lock-free skiplist that allows concurrent reads during writes
  • Concurrent inserts: When allow_concurrent_memtable_write is enabled, multiple writers insert their batches simultaneously using CAS-based skiplist insertion
  • Thread-local batching: Concurrent mode uses MemTablePostProcessInfo to batch atomic counter updates in thread-local storage, reducing contention
  • Bloom filter: Prefix and/or whole-key bloom filter reduces memtable lookups on misses

WAL

  • Pre-computed type CRC: type_crc_[kMaxRecordType+1] avoids recomputing the CRC of the type byte on every write
  • CRC combine: Uses crc32c::Crc32cCombine() to efficiently merge pre-computed type CRC with payload CRC
  • Optional compression: WAL supports streaming compression (ZSTD only; other algorithms are not supported for WAL streaming and are reset to kNoCompression by option sanitization) to reduce I/O bandwidth

Write Amplification

Write amplification is the ratio of bytes written to storage vs bytes written by the application.

Sources:

ComponentAmplificationNotes
WAL1.0xEach byte written once (plus CRC/header overhead)
Flush (memtable to L0)~1.0xDirect serialization to SST
L0 to L1 compaction~1 + L1_size/L0_sizeReads all L0 files + overlapping L1 files; per-byte amplification is roughly constant (~2x) in steady state
L1+ leveled compactionO(fanout) per levelDefault fanout is 10 (see max_bytes_for_level_multiplier)

Total write amplification: Typically 10-30x for leveled compaction, 2-10x for universal compaction.

Throughput Tuning

Write Buffer Configuration

OptionEffectTradeoff
write_buffer_sizeLarger memtable = fewer L0 flushesMore memory usage; longer recovery time
max_write_buffer_numberMore immutable memtables before stallMore memory; delayed flush may increase L0 pressure
min_write_buffer_number_to_mergeMerge multiple memtables in one flushReduces write amplification; increases flush latency

Write Mode Selection

OptionEffectTradeoff
enable_pipelined_writeOverlap WAL and memtable phasesHigher throughput; slightly more complex failure handling
unordered_writeIndependent memtable insertsMaximum throughput; relaxed inter-writer ordering
allow_concurrent_memtable_writeParallel memtable insertion within a groupHigher throughput for multi-writer workloads
max_write_batch_group_size_bytesControls maximum group sizeLarger groups amortize sync cost; may increase tail latency

WAL Configuration

OptionEffectTradeoff
sync (WriteOptions)fsync after each write groupDurability guarantee; significant latency cost
disableWAL (WriteOptions)Skip WAL entirelyMaximum write speed; no crash recovery for this write
manual_wal_flushApplication controls when WAL flushesBetter batching; risk of data loss on crash
recycle_log_file_numReuse old WAL filesAvoids filesystem allocation overhead
wal_compressionCompress WAL recordsReduces I/O bandwidth; adds CPU cost

Flow Control Tuning

OptionEffectTradeoff
level0_slowdown_writes_triggerL0 file count before delayHigher value = more L0 accumulation before slowdown
level0_stop_writes_triggerL0 file count before full stopHigher value = more L0 files tolerated
soft_pending_compaction_bytes_limitDelay threshold for pending bytesHigher value = more tolerance for compaction debt
hard_pending_compaction_bytes_limitStop threshold for pending bytesHigher value = more risk of space amplification
delayed_write_rateInitial write rate during delayLower value = more aggressive throttling

Benchmarking with db_bench

Key db_bench flags for write workloads:

BenchmarkDescription
fillseqSequential key insertion
fillrandomRandom key insertion
overwriteOverwrite existing random keys
fill100KLarge-value writes (100 KB)

Example for benchmarking write throughput:

# Build release binary
make clean && DEBUG_LEVEL=0 make -j128 db_bench

# Sequential write throughput
./db_bench --benchmarks=fillseq --num=10000000 --value_size=100 \
  --compression_type=none --disable_wal=false --sync=false

# Random write throughput with concurrent writers
./db_bench --benchmarks=fillrandom --num=10000000 --threads=8 \
  --allow_concurrent_memtable_write=true --enable_pipelined_write=true

Cross-Component Interactions

FromToInteraction
WriteImplWriteThreadLeader election, group batching
WriteImpllog::WriterSerialize WriteBatch via AddRecord()
WriteImplMemTableInsert via WriteBatchInternal::InsertInto()
WriteImplWriteControllerCheck IsStopped() / NeedsDelay() in PreprocessWrite()
WriteImplWriteBufferManagerCheck ShouldFlush() / ShouldStall()
MemTableFlushJobFlush immutable memtables to L0 SST
FlushJobVersionSetLogAndApply(VersionEdit) commits flush to MANIFEST
FlushJobWriteBufferManagerFreeMem() releases memory, may end stall
FlushJobCompactionPickerNew L0 file may trigger compaction
CompactionJobWriteControllerRecalculateWriteStallConditions() updates tokens