Choosing a RocksDB Compaction Style for Your Workload
Guide to picking Level, Universal, or FIFO compaction in RocksDB based on read/write ratio, latency, and storage constraints, with sample C++/Java code and verification steps.
05 Dec 2025, 15:07 UTC

Decision and Constraints
Select a RocksDB compaction style that matches your workload’s read/write ratio, latency sensitivity, storage cost tolerance, and acceptable background CPU usage. The three built‑in styles are:
- Level compaction – maintains sorted levels, optimizing read amplification.
- Universal compaction – merges files based on size ratios, balancing read and write amplification.
- FIFO compaction – evicts the oldest files when a size threshold is crossed, minimizing write amplification.
If you cannot tolerate high read amplification (e.g., latency‑critical reads) avoid FIFO. If write throughput is paramount and you can spare disk space, consider FIFO. For mixed workloads, Universal is often a good starting point.
Comparison Table
| Compaction Style | Read Amplification | Write Amplification | Space Amplification | Typical Use Case |
|---|---|---|---|---|
| Level | Low (≈1‑2) | Medium‑High (due to level‑wise merges) | Low (≈1‑1.2) | Read‑heavy workloads, point lookups, range scans |
| Universal | Moderate (≈2‑4) | Moderate (≈1‑2) | Moderate (≈1.2‑1.5) | Mixed read/write, write‑bursts with occasional reads |
| FIFO | High (≈5‑10+) | Low (≈0.5‑1) | High (≈2‑5+) | Write‑heavy log‑like workloads, append‑only logs, temporary caches |
Trade‑offs
- Level gives the smallest read and space amplification but incurs higher write amplification and more background CPU because each level must be kept sorted.
- Universal reduces write amplification relative to Level while keeping read amplification reasonable; background CPU usage is moderate and depends on the chosen size ratio.
- FIFO minimizes write amplification and background CPU, but the lack of sorting leads to high read amplification and larger disk usage.
Choose the style that aligns with the dominant constraint in your environment. If you are unsure, start with Universal and tune the target_file_size_base and max_bytes_for_level_base parameters.
Implementation
C++ (RocksDB ≥ 8.0)
#include
#include
rocksdb::Options opts;
opts.create_if_missing = true;
// Example: choose Universal compaction
opts.compaction_style = rocksdb::kUniversalCompaction;
// Tune size‑ratio based universal compaction
opts.target_file_size_base = 64 << 20; // 64 MiB
opts.max_bytes_for_level_base = 256 << 20; // 256 MiB
// Limit background threads to avoid CPU saturation
opts.max_background_compactions = 2;
rocksdb::DB* db;
rocksdb::Status s = rocksdb::DB::Open(opts, "/path/to/db", &db);
if (!s.ok()) {
// handle error
}
Run the code with a user that has read/write permission on the directory /path/to/db. Excessive background compaction can increase CPU usage; monitor top or pidstat to ensure it stays below your threshold.
Java (RocksDB Java ≥ 8.0)
import org.rocksdb.Options;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;
Options opts = new Options();
opts.setCreateIfMissing(true);
opts.setCompactionStyle(Options.CompactionStyle.UNIVERSAL);
opts.setTargetFileSizeBase(64L * 1024 * 1024); // 64 MiB
opts.setMaxBytesForLevelBase(256L * 1024 * 1024); // 256 MiB
opts.setMaxBackgroundCompactions(2);
try {
RocksDB db = RocksDB.open(opts, "/path/to/db");
// use db
} catch (RocksDBException e) {
// handle error
}
Same permission and monitoring considerations apply.
Verification and Tuning
After opening the DB, you can check that the selected compaction style is active and observe amplification metrics:
- Pending compactions (should be non‑zero if background work is happening):
std::string pending = db->GetProperty("rocksdb.compaction.pending"); // Java equivalent: db.getProperty("rocksdb.compaction.pending") - Read amplification – approximate via
rocksdb.read-amplificationproperty. - Write amplification –
rocksdb.write-amplificationproperty. - Space amplification – ratio of live data size to total file size, obtainable from
rocksdb.live-sst-files-sizeandrocksdb.total-sst-files-size.
Run a representative benchmark (e.g., db_bench) with workloads matching your production mix:
# Example: fillrandom + readrandom + deleterandom
./db_bench --benchmarks=fillrandom,readrandom,deleterandom \
--db=/path/to/db --use_existing_db=1 \
--num=1000000 --value_size=100
Collect the latency and amplification numbers. If read latency is higher than expected, consider switching to Level or decreasing target_file_size_base to reduce read amplification. If write latency spikes, increase max_background_compactions or move to FIFO (accepting higher space usage). Iterate until the observed trade‑offs match the table’s expectations for your workload.
Limitations and Rollback
Changing the compaction style on an existing database is not supported in‑place; attempting to alter opts.compaction_style on a live DB will cause corruption. To switch styles you must:
- Create a new DB instance with the desired options.
- Migrate data (e.g., using
rocksdb::BackupEngineor a custom copy loop). - Switch the application to the new DB and delete the old one after verification.
Because the operation creates a new copy, a simple rollback is to retain the original DB directory until the migration is validated. If validation fails, simply continue using the original DB and discard the new copy.
Monitor disk usage during migration; ensure you have at least 2× the current DB size free to accommodate both copies temporarily.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.