Level‑style vs Universal‑style Compaction: Choosing the Right Strategy for Bulk‑Load to Steady‑State Transition in RocksDB
0 reputation · 13 Mar 2022, 07:20 UTC
0 reputation · 13 Mar 2022, 07:20 UTC
35275 reputation · 13 Mar 2022, 15:40 UTC
RocksDB does not automatically change compaction style after a bulk-load phase finishes. The compaction style (level or universal) is fixed at column-family creation and remains unchanged for the lifetime of that column family. There are no version-specific thresholds or conditions that trigger an automatic switch from level-style to universal-style compaction during steady state. If you need to change styles, you must recreate the column family (or the entire database), which requires downtime or a parallel ingestion path.
RocksDB's compaction picker is initialized once when the column family is created. The picker's logic—whether it organizes files into leveled tiers (level compaction) or size-based buckets (universal compaction)—is baked into the column family's CompactionStyle enum value. No background thread, configuration flag, or heuristic monitors the workload phase and flips this setting. This behavior is consistent across RocksDB 6.x, 7.x, and 8.x releases; experimental flags for mid-life style changes exist only in development branches and are not production-stable.
universal_compaction_size_ratio (default 1%) and universal_compaction_min_merge_width are tuned. The total on-disk size stays closer to the logical data size.max_bytes_for_level_base and bloom filters.max_bytes_for_level_multiplier (default 10) and max_bytes_for_level_base are not carefully sized for your SSD.Since an in-place style change is impossible, use a side-by-side comparison on a copy of production data:
ColumnFamilyOptions cf_opts;
cf_opts.compaction_style = kCompactionStyleUniversal; // or kCompactionStyleLevel
cf_opts.universal_compaction_size_ratio = 1; // tune for your SSD
cf_opts.max_bytes_for_level_base = 256 << 20; // 256 MB
cf_opts.target_file_size_base = 64 << 20; // 64 MB
Status s = db->CreateColumnFamily(cf_opts, "test_cf", &test_handle);
db->GetIntProperty(test_handle, "rocksdb.bytes-written") / user_bytes_writtendb->GetIntProperty(test_handle, "rocksdb.block.cache.miss") / total_readsdb->GetApproximateSizes(...) vs logical key-size sumDB::GetHistogram or Prometheus exporters to observe L0 file counts, compaction frequency, and stall occurrences.What is your steady-state write throughput (MiB/s) and key distribution pattern (uniform, skewed, time-series)? This determines whether universal compaction's bucket merging will actually reduce write amplification for your workload, or if level compaction's tighter read amplification is worth the extra writes.
universal_compaction_size_ratio and max_bytes_for_level_base to bound space amplification.Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 13 Mar 2022, 16:49 UTC
Since the compaction style is static per column family, any transition from a bulk-load optimized setup to a steady-state configuration requires a manual migration. It is important to note that using the SetCompactOptions API to update parameters or recreating the column family to change styles can trigger a massive compaction cycle.
For those operating under tight SSD capacity, this transition can lead to a temporary spike in space amplification that may exceed available disk overhead. To verify the impact without risking production downtime, consider these steps:
INFO_LOG to track compaction_jobs and bytes_written during a test migration on a cloned dataset.GetProperty API for rocksdb.compactionstyle to ensure the active style matches your intended configuration across different RocksDB versions (specifically 6.x+).universal_compaction_size_ratio to balance the write-amplification reduction against the risk of disk exhaustion.