一、RocksDB项目介绍
RocksDB是由Facebook团队开发的一个嵌入式、持久化的键值(Key-Value)存储数据库,其核心设计基于Google的LevelDB项目。
当时为了应对大规模数据存储与高并发写入场景时遇到的性能瓶颈,而传统的基于B-Tree结构的数据库在随机写入场景下性能表现不佳,因此,Facebook团队基于基于LevelDB的LSM-Tree(Log-Structured Merge-Tree)架构进行深度优化,开发出了RocksDB项目。
RocksDB于2013年正式开源,其开发目标包括:提供高性能的键值存储(尤其适用于闪存存储)、支持快照与事务、支持自定义合并操作、兼容多种编程语言和操作系统等。RocksDB存储引擎开源后迅速在TiKV/TiDB、Kafka Streams、Flink等大型项目中被广泛应用。
RocksDB数据库的诞生是Facebook为解决其业务中的写入性能瓶颈,并充分利用现代存储硬件的特性而启动的工程实践。它不仅继承了LevelDB的优雅设计,又在并发控制、内存管理、压缩算法等方面进行了大幅改进,成为当前最流行的LSM-Tree存储引擎之一。

RocksDB常见的应用场景:
分布式数据库的本地存储,如TiKV等,每个分布式节点采用RocksDB存储分片数据。
写密集业务的日志存储。
区块链开发,构建区块索引。
本地持久化缓存和快照保存。
二、RocksDB架构拆解
RocksDB项目的架构核心是LSM-Tree(Log-Structured Merge-Tree),它是一种面向写密集负载的多层有序结构,可以将随机写转换为顺序写,从而获得极高的写入吞吐量。
RocksDB项目的设计要点:
按顺序写内存:所有数据的写入先写到WAL和Memtable上,避免随机写,适应SSD的硬件特性。
延迟合并:通过Compaction异步整理数据,平衡三种放大因子(写放大、读放大、空间放大)。
负载参数可配置:早期配置继承自LevelDB,后逐步解耦并加入参数如write_buffer_size等。

1.先写日志WAL(Write-Ahead Log)
保证写入的持久性,程序崩溃后可通过WAL恢复Memtable中的数据。
2.Memtable与Immutable Memtable
Memtable:内存中的写缓存,采用跳表(SkipList)实现,支持高效的写入和范围查询。所有写入操作首先写入Memtable,同时写入WAL以保证持久性。
Immutable Memtable:当Memtable写满后,它变为只读的Immutable Memtable,后台线程负责将其刷入磁盘,形成SST文件。
3.Flush流程
后台线程把Immutable MemTable按顺序写出,生成一个Level 0层的SST文件,然后丢掉该MemTable和对应的WAL,并开启新的WAL。
4.SST(Sorted String Table)文件
每个SST文件内部按Key排序存储,并包含索引、布隆过滤器等元数据,以加速查找。
SST默认的块文件大小约4KB,支持Zlib、BZ2、Snappy、LZ4、ZSTD等压缩方式。
SST块带有checksum校验值,可以防止损坏和截断。
5.压缩合并策略(Compaction)
Compaction是LSM-Tree的核心,用于解决三个放大因子之间的trade-off:
写放大:每次写入实际写入磁盘的数据量 / 用户写入数据量。
读放大:读取一个 Key 需要读取的磁盘块数。
空间放大:已删除或过期数据占用的冗余空间。
Compaction用k-way merge策略合并有序SST,输出新的SST,并在合适时机丢掉过期版本。
常见的三种Compaction策略:
Leveled Compaction:默认策略,将数据逐层合并,每层大小按固定倍数增长,如10倍,写放大适中,读放大较小。
Universal Compaction:适用于写入密集场景,合并所有层级为一个大的SST,写放大低,但读放大较高。
FIFO Compaction:类似队列,过期数据直接丢弃,适合TTL场景。

RocksDB写数据流程:
step.01:写入WAL,按顺序写。
step.02:写入Memtable,Memtable被写满后转为Immutable Memtable。
step.03:后台Flush为L0层SST文件。
step.04:通过Compaction策略将数据下沉。
RocksDB读数据流程:
step.01:查询Memtable。
step.02:查询Immutable Memtable。
step.03:查询L0层所有SST文件,检查布隆过滤器。
step.04:逐层向下查询,若命中则返回结果,否则返回Not Found。
RocksDB读写流程图

三、RocksDB核心接口

1.打开与关闭数据库
cpp
// RocksDB 11+ 已移除 DB** 重载,统一使用 unique_ptr 接管所有权
static Status DB::Open(const Options& options,
const std::string& name,
std::unique_ptr<DB>* dbptr);
static Status DB::OpenForReadOnly(const Options& options,
const std::string& name,
std::unique_ptr<DB>* dbptr,
bool error_if_wal_file_exists = false);
static Status DB::OpenAsSecondary(const Options& options,
const std::string& name,
const std::string& secondary_path,
std::unique_ptr<DB>* dbptr);
Status DB::Close(); // 可选,析构也会关闭
Open():
常规读写打开;目录不存在时可通过"create_if_missing=true"创建。
OpenForReadOnly():
只读打开,适合备份校验、只读副本工具, 不写 WAL/不触发compaction。
OpenAsSecondary():
二级实例, 从库跟随主库SST/MANIFEST,用于在线只读扩展。
Close():
显式关闭,便于捕获关闭阶段错误,能获取到错误对应的Status。
代码样例:
cpp
rocksdb::Options options;
options.create_if_missing = true;
options.error_if_exists = false;
// 常用性能相关配置
options.IncreaseParallelism(std::thread::hardware_concurrency());
options.OptimizeLevelStyleCompaction();
std::unique_ptr<rocksdb::DB> db;
auto s = rocksdb::DB::Open(options, "/var/lib/myapp/rocksdb", &db);
2.更新与删除
cpp
Status Put(const WriteOptions& options,
ColumnFamilyHandle* column_family,
const Slice& key,
const Slice& value);
Status Delete(const WriteOptions& options,
ColumnFamilyHandle* column_family,
const Slice& key);
Status SingleDelete(const WriteOptions& options,
ColumnFamilyHandle* column_family,
const Slice& key);
Status Merge(const WriteOptions& options,
ColumnFamilyHandle* column_family,
const Slice& key,
const Slice& value);
Put():
写入或覆盖一个Key-Value对。
Delete():
删除对应的Key,范围内可能触发多次删除。
SingleDelete():
仅当该key只被Put过一次时使用,可减少部分Compaction开销。
Merge():
合并多个Value,适合计数器、列表追加等场景。
代码样例:
cpp
WriteOptions wo;
wo.sync = false; // 默认异步落盘
db->Put(wo, "k1", "v1");
// key不存在也返回OK
db->Delete(wo, "k1");
// 保证只写过一次的场景,可更快回收
db->SingleDelete(wo, "k1");
// 需配置merge_operator
db->Merge(wo, "counter", "1");
3.读操作
cpp
Status Get(const ReadOptions& options,
ColumnFamilyHandle* column_family,
const Slice& key,
std::string* value);
std::vector<Status> MultiGet(const ReadOptions& options,
ColumnFamilyHandle* column_family,
const std::vector<Slice>& keys,
std::vector<std::string>* values);
bool KeyMayExist(const ReadOptions& options,
ColumnFamilyHandle* column_family,
const Slice& key,
std::string* value,
bool* value_found = nullptr);
Get():
查询单个key。
MultiGet():
批量点查,一次调用获取多个键的值,显著快于循环Get。
KeyMayExist():
结合Bloom,快速判断Key可能存在,适合过滤明显不存在的key。
代码样例:
cpp
rocksdb::ReadOptions ropt;
ropt.fill_cache = true;
ropt.verify_checksums = true;
std::string value;
auto s = db->Get(ropt, "session:abc", &value);
std::vector<rocksdb::Slice> keys = {"a", "b", "c"};
std::vector<std::string> values;
auto statuses = db->MultiGet(ropt, keys, &values);
for (size_t i = 0; i < keys.size(); ++i) {
if (statuses[i].ok()) {
std::cout << keys[i].ToString() << " => " << values[i] << "\n";
}
}
4.原子批量写操作
cpp
class WriteBatch {
public:
Status Put(const Slice& key, const Slice& value);
Status Delete(const Slice& key);
Status Merge(const Slice& key, const Slice& value);
void Clear();
};
Status Write(const WriteOptions& options, WriteBatch* updates);
将多个Put/Delete组合为一个原子操作,减少IO次数和WAL次数,提升吞吐量,适合一次业务变更涉及多个key的场景。
代码样例:
cpp
rocksdb::WriteBatch batch;
batch.Put("key2", "value2");
batch.Delete("key1");
db->Write(rocksdb::WriteOptions(), &batch);
5.扫描与前缀遍历
cpp
Iterator* NewIterator(const ReadOptions& options,
ColumnFamilyHandle* column_family);
// Iterator 常用方法:
void SeekToFirst();
void SeekToLast();
void Seek(const Slice& target);
void SeekForPrev(const Slice& target);
void Next();
void Prev();
bool Valid() const;
常用操作:Seek/SeekForPrev/SeekToFirst/SeekToLast/Next/Prev。
支持有序扫描、范围查询、前缀遍历、反向遍历等操作。
代码样例:
cpp
rocksdb::ReadOptions ropt;
// 前缀扫描优化:需开启prefix_extractor
ropt.prefix_same_as_start = true;
ropt.total_order_seek = false;
std::unique_ptr<rocksdb::Iterator> it(db->NewIterator(ropt));
for (it->Seek("user:"); it->Valid() && it->key().starts_with("user:"); it->Next()) {
std::cout << it->key().ToString() << " => " << it->value().ToString() << "\n";
}
if (!it->status().ok()) {
std::cerr << it->status().ToString() << "\n";
}
6.SnapShot快照
cpp
const Snapshot* GetSnapshot();
void ReleaseSnapshot(const Snapshot* snapshot);
// 与ReadOptions配合
ReadOptions options;
options.snapshot = snapshot;
获得某一时刻的一致性只读视图,适合备份扫描、报表导出、长事务只读阶段。
代码样例:
cpp
const Snapshot* snap = db->GetSnapshot();
ReadOptions ro;
ro.snapshot = snap;
//看到创建快照时的版本
db->Get(ro, "k", &v);
db->ReleaseSnapshot(snap);
7.事务处理
cpp
Status Transaction::Get(const ReadOptions&,
const Slice& key, std::string* value);
Status Transaction::Put(const Slice& key, const Slice& value);
Status Transaction::Delete(const Slice& key);
Status Transaction::Commit();
Status Transaction::Rollback();
RocksDB提供了TransactionDB类,用于多行操作的原子性和隔离性。
代码样例:
cpp
rocksdb::TransactionDB* txn_db;
rocksdb::Transaction* txn = txn_db->BeginTransaction(rocksdb::WriteOptions());
txn->Put("key1", "value_new");
txn->Delete("key2");
txn->Commit(); // 原子提交
delete txn;
8.完整代码示例
cpp
#include "rocksdb/db.h"
#include "rocksdb/write_batch.h"
using namespace ROCKSDB_NAMESPACE;
int main() {
Options options;
options.create_if_missing = true;
options.IncreaseParallelism();
options.OptimizeLevelStyleCompaction();
std::unique_ptr<DB> db;
Status s = DB::Open(options, "/tmp/rocksdb_demo", &db);
assert(s.ok());
s = db->Put(WriteOptions(), "key1", "value1");
assert(s.ok());
PinnableSlice val;
s = db->Get(ReadOptions(), db->DefaultColumnFamily(), "key1", &val);
assert(s.ok() && val == "value1");
val.Reset();
WriteBatch batch;
batch.Delete("key1");
batch.Put("key2", "value1");
s = db->Write(WriteOptions(), &batch);
assert(s.ok());
{
std::unique_ptr<Iterator> it(db->NewIterator(ReadOptions()));
for (it->SeekToFirst(); it->Valid(); it->Next()) {
// ...
}
}
db->Close();
}
补充:Options中最常用的调参项
cpp
rocksdb::Options options;
options.create_if_missing = true;
// 写路径
options.write_buffer_size = 64 << 20; //MemTable 大小
options.max_write_buffer_number = 3;
options.min_write_buffer_number_to_merge = 1;
// 压缩与层级
options.level0_file_num_compaction_trigger = 4;
options.max_bytes_for_level_base = 256 << 20;
options.target_file_size_base = 64 << 20;
options.compression = rocksdb::kLZ4Compression;
options.bottommost_compression = rocksdb::kZSTD;
// 后台线程
options.max_background_jobs = 8;
options.max_subcompactions = 2;
四、RocksDB开发环境安装
方式1:包管理器安装
bash
# Ubuntu 22.04+ / Debian 12+
sudo apt update
sudo apt install -y librocksdb-dev
# Fedora
sudo dnf install rocksdb-devel
# Arch
sudo pacman -S rocksdb
方式2:源码编译安装(更推荐这种)
cpp
# 源码下载
git clone https://github.com/facebook/rocksdb.git
cd rocksdb
# 编译与安装
mkdir build && cd build
cmake ..
make -j$(nproc)
make static_lib && sudo make install-static
make shared_lib && sudo make install-shared
常用的编译命令:
bash
g++ -std=c++20 demo.cpp -o demo -lrocksdb -lpthread -ldl -lz -lbz2 -lsnappy -llz4 -lzstd
五、RocksDB编码实战
Demo1.Rall风格的C++代码封装
cpp
#include <rocksdb/db.h>
#include <rocksdb/options.h>
#include <rocksdb/slice.h>
class RocksStore {
public:
struct Config {
std::string path = "/var/lib/myapp/rocks";
size_t block_cache_mb = 256;
bool sync_wal = false;
};
explicit RocksStore(Config cfg) : cfg_(std::move(cfg)) {
rocksdb::DBOptions db_opt;
db_opt.create_if_missing = true;
db_opt.create_missing_column_families = true;
db_opt.max_background_jobs = 4;
db_opt.IncreaseParallelism();
rocksdb::ColumnFamilyOptions cf_opt;
cf_opt.compression = rocksdb::kLZ4Compression;
cf_opt.bottommost_compression = rocksdb::kZSTD;
rocksdb::BlockBasedTableOptions table_opt;
table_opt.block_cache = rocksdb::NewLRUCache(cfg_.block_cache_mb << 20);
table_opt.filter_policy.reset(rocksdb::NewBloomFilterPolicy(10, false));
table_opt.cache_index_and_filter_blocks = true;
cf_opt.table_factory.reset(NewBlockBasedTableFactory(table_opt));
std::vector<rocksdb::ColumnFamilyDescriptor> descs = {
{rocksdb::kDefaultColumnFamilyName, cf_opt},
{"meta", cf_opt},
{"data", cf_opt},
};
auto s = rocksdb::DB::Open(db_opt, cfg_.path, descs, &handles_, &db_);
if (!s.ok()) throw std::runtime_error("open rocksdb: " + s.ToString());
}
~RocksStore() {
for (auto* h : handles_) delete h;
}
rocksdb::Status PutData(const std::string& key, const std::string& value) {
rocksdb::WriteOptions w;
w.sync = cfg_.sync_wal;
return db_->Put(w, handles_[2], key, value);
}
rocksdb::Status GetData(const std::string& key, std::string* value) {
return db_->Get(rocksdb::ReadOptions(), handles_[2], key, value);
}
rocksdb::Status PutMeta(const std::string& key, const std::string& value) {
return db_->Put(rocksdb::WriteOptions(), handles_[1], key, value);
}
rocksdb::DB* raw() { return db_.get(); }
private:
Config cfg_;
std::unique_ptr<rocksdb::DB> db_;
std::vector<rocksdb::ColumnFamilyHandle*> handles_;
};
Demo2.基于RocksDB的用户订单前缀扫描
cpp
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <rocksdb/db.h>
#include <rocksdb/options.h>
#include <rocksdb/slice_transform.h>
namespace {
constexpr size_t kOrderNamespacePrefixLen = 6;
}
struct Order {
std::string order_id;
std::string payload;
};
std::string MakeOrderKey(const std::string& user_id, const std::string& order_id) {
return "order:" + user_id + ":" + order_id;
}
std::vector<Order> ListUserOrders(rocksdb::DB* db, const std::string& user_id,
size_t limit) {
const std::string prefix = "order:" + user_id + ":";
rocksdb::ReadOptions ropt;
ropt.total_order_seek = true;
std::vector<Order> result;
std::unique_ptr<rocksdb::Iterator> it(db->NewIterator(ropt));
for (it->Seek(prefix); it->Valid() && result.size() < limit; it->Next()) {
const std::string key = it->key().ToString();
if (key.compare(0, prefix.size(), prefix) != 0) {
break;
}
Order order;
order.order_id = key.substr(key.find_last_of(':') + 1);
order.payload = it->value().ToString();
result.push_back(std::move(order));
}
return result;
}
void PrintOrders(const std::string& user_id, const std::vector<Order>& orders) {
std::cout << "\n=== 用户 " << user_id << " 的订单列表(共 " << orders.size()
<< " 条)===\n";
if (orders.empty()) {
std::cout << "(空)\n";
return;
}
for (size_t i = 0; i < orders.size(); ++i) {
std::cout << " [" << (i + 1) << "] order_id=" << orders[i].order_id << "\n"
<< " payload=" << orders[i].payload << "\n";
}
}
int main() {
const std::string db_path = "/tmp/rocks_orders_cpp";
rocksdb::Options options;
options.create_if_missing = true;
options.prefix_extractor.reset(
rocksdb::NewFixedPrefixTransform(kOrderNamespacePrefixLen));
// RocksDB 11+:Open 输出参数为 std::unique_ptr<DB>*,已移除 DB** 重载
std::unique_ptr<rocksdb::DB> db;
rocksdb::Status s = rocksdb::DB::Open(options, db_path, &db);
if (!s.ok()) {
std::cerr << "Open failed: " << s.ToString() << "\n";
return 1;
}
rocksdb::WriteOptions wopt;
db->Put(wopt, MakeOrderKey("u42", "20260723001"),
R"({"amount":99.0,"item":"键盘","status":"paid"})");
db->Put(wopt, MakeOrderKey("u42", "20260723002"),
R"({"amount":20.0,"item":"鼠标垫","status":"paid"})");
db->Put(wopt, MakeOrderKey("u42", "20260724003"),
R"({"amount":199.0,"item":"耳机","status":"shipped"})");
db->Put(wopt, MakeOrderKey("u99", "20260723001"),
R"({"amount":9.9,"item":"数据线","status":"paid"})");
db->Put(wopt, MakeOrderKey("u99", "20260725002"),
R"({"amount":59.0,"item":"保护壳","status":"cancelled"})");
db->Put(wopt, MakeOrderKey("u07", "20260721001"),
R"({"amount":1299.0,"item":"显示器","status":"paid"})");
std::cout << "打开 RocksDB: " << db_path << "\n";
std::cout << "写入演示订单数据完成。\n";
for (const auto& uid : {"u42", "u99", "u07", "u_not_exist"}) {
PrintOrders(uid, ListUserOrders(db.get(), uid, 100));
}
auto limited = ListUserOrders(db.get(), "u42", 2);
std::cout << "\n=== 用户 u42 前缀扫描 limit=2 ===\n";
for (const auto& order : limited) {
std::cout << " " << order.order_id << " -> " << order.payload << "\n";
}
std::cout << "\n订单查询完成。\n";
return 0;
}
代码运行结果:

Demo3.最简单的写入 & 查询用例
cpp
#include <iostream>
#include <memory>
#include <string>
#include <rocksdb/db.h>
int main() {
std::unique_ptr<rocksdb::DB> db;
rocksdb::Options options;
options.create_if_missing = true;
rocksdb::Status s = rocksdb::DB::Open(options, "/tmp/rocksdb_basic", &db);
if (!s.ok()) {
std::cerr << "Open failed: " << s.ToString() << "\n";
return 1;
}
s = db->Put(rocksdb::WriteOptions(), "user:1001", R"({"name":"alice","age":32})");
if (!s.ok()) {
std::cerr << "Put failed: " << s.ToString() << "\n";
}
std::string value;
s = db->Get(rocksdb::ReadOptions(), "user:1001", &value);
if (s.ok()) {
std::cout << "value = " << value << "\n";
} else if (s.IsNotFound()) {
std::cout << "key not found\n";
} else {
std::cerr << "Get failed: " << s.ToString() << "\n";
}
// unique_ptr 析构时自动 Close,无需手动 delete
return 0;
}
代码运行结果:
cpp
value = {"name":"alice","age":32}
六、常见问题排查
Q1:Open失败 & Open时进程崩溃
可能"WAL/MANIFEST/SST"不一致。先尝试备份目录,再用ldb工具诊断,必要时从Backup/Checkpoint检查点恢复数据,请勿直接删MANIFEST。
Q2:写入变慢或出现Stall
可能是Compaction/Flush跟不上了。检查磁盘利用率、max_background_jobs、L0文件数等参数。观察rocksdb.stall相关统计。
Q3:Get查询速度很慢
Block Cache过小、未打开Bloom布隆过滤器、查询的value过大、层级过多导致读放大。用PerfContext看主要时间花在了block read还是mutex步骤上。
Q4:Iterator查看到旧数据或漏数据
确认是否设置了Snapshot快照。
Q5:多进程同时写同一目录
RocksDB默认不支持多进程写。如果需要多进程写,请用二级实例、或外置服务化、文件锁等策略自行串行化处理。
Q6:编译时,链接报缺少snappy/lz4/zstd
按编译RocksDB时启用的压缩库补全-lsnappy -llz4 -lzstd -lbz2 -lz等依赖库。
Q7:编译报"error: no matching function for call to 'rocksdb::DB::Open(rocksdb::Options&, const std::string&, rocksdb::DB**)'"
RocksDB 11.0以后的版本已移除"DB**"形式的Open/OpenForReadOnly/OpenAsSecondary等重载,改为输出智能指针"std::unique_ptr<DB>*"。
七、RocksDB与其他数据库选型方案

参考阅读:
官方Wiki:https://github.com/facebook/rocksdb/wiki
Basic Operations:https://github.com/facebook/rocksdb/wiki/Basic-Operations
Column Families:https://github.com/facebook/rocksdb/wiki/Column-Families
Transactions:https://github.com/facebook/rocksdb/wiki/Transactions
Tuning Guide:https://github.com/facebook/rocksdb/wiki/RocksDB-Tuning-Guide
Checkpoints:https://github.com/facebook/rocksdb/wiki/Checkpoints