LevelDB数据库操作需严格检查Status返回值、确保字符串生命周期、正确管理指针及线程安全:Open后必须检查status.ok();Put/Get避免悬垂指针;Get前无需初始化value字符串;关闭时须delete db且不可重复;Iterator非线程安全。

打开 LevelDB 数据库时必须检查 status 返回值
LevelDB 的 DB::Open() 不会抛异常,而是通过 leveldb::Status 返回结果。忽略它几乎必然导致后续操作崩溃或静默失败。
常见错误是直接用 new DB 或跳过检查:
leveldb::DB* db; leveldb::Options options; options.create_if_missing = true; leveldb::Status status = leveldb::DB::Open(options, "/path/to/db", &db); // ❌ 错误:没检查 status.ok() db->Put(...); // 此时 db 可能为 nullptr,段错误
正确做法是显式判断:
-
status.ok()为真才继续;否则用status.ToString()查看具体错误(如权限不足、路径不存在、文件被占用) -
options.create_if_missing = true是必需的,否则路径不存在时直接报错IO error: ... No such file or directory - 数据库路径不能是已存在的普通文件,必须是空目录或不存在的路径(LevelDB 会在其中创建多个文件)
写入字符串键值对要留意 std::string 的生命周期
LevelDB 的 Put() 和 Get() 接口接受 leveldb::Slice,它只是指向内存的“视图”,不管理内存所有权。传入临时 std::string 的 c_str() 极易引发悬垂指针。
立即学习“C++免费学习笔记(深入)”;
典型错误写法:
db->Put(write_options, leveldb::Slice("key"), leveldb::Slice(std::string("value").c_str()));
// ❌ std::string 临时对象立即析构,c_str() 指向无效内存安全做法:
- 用命名的
std::string变量,并确保其生命周期覆盖整个Put()调用 - 或直接用字符串字面量(C-style string),因为它们存储在只读段,生命周期全局
-
WriteOptions中sync = true可保证写入落盘,但显著降低性能;默认false(仅写入 OS 缓冲区)
读取时 Get() 返回的 value 是栈上拷贝,无需手动释放
Get() 第三个参数是 std::string*,LevelDB 会把查到的值追加(append)进去。它不复用原字符串内容,而是先清空再写入,所以调用前无需初始化该字符串。
示例:
std::string value;
leveldb::Status s = db->Get(read_options, "key", &value);
if (s.ok()) {
printf("Found value: %s\n", value.c_str()); // ✅ value 已含完整数据
} else if (s.IsNotFound()) {
printf("Key not found\n"); // ✅ 用 IsNotFound() 判断缺失,而非检查 value.empty()
} else {
fprintf(stderr, "Read error: %s\n", s.ToString().c_str());
}注意点:
- 不要传入
nullptr给 value 参数,会导致段错误 -
read_options.fill_cache = false可避免读取污染 LRU 缓存,适合一次性扫描类场景 - 查询不存在的 key 时,
status是NotFound(),不是ok(),也不是空字符串
关闭数据库前必须显式 delete db,且不能重复 delete
LevelDB 使用裸指针管理实例,没有 RAII 封装。忘记 delete 会导致资源泄漏(句柄、内存、mmap 区域);重复 delete 直接崩溃。
推荐模式:
- 用
std::unique_ptr<:db void>自定义删除器,但需注意 LevelDB 不提供标准 deleter,得自己写[](leveldb::DB* p) { delete p; } - 更简单的是作用域控制:在函数末尾或 RAII 类析构中统一
delete db,并立即将指针置为nullptr - 关闭后不能再调用任何方法(包括
Get),否则行为未定义 —— 即使指针还没被覆写,也可能访问已释放内存
最易被忽略的一点:LevelDB 的写操作(Put/Delete)在多线程下是安全的,但 Iterator 实例**不是线程安全的**,每个线程应创建独立 iterator。











