【存储引擎】LevelDB 编译使用

代码获取

LevelDB 的仓库地址为 google/leveldb(github.com)

下载代码的时候最好加上 --recurse-submodules 参数,直接把子模块的内容一并下载下来:

git clone --recurse-submodules https://github.com/google/leveldb.git

编译会依赖子模块里的代码,缺少则无法通过编译。

代码结构

leveldb 代码结构如下,最重要的是 db、table、util 三个文件夹,里面包含了 leveldb 最核心的代码实现,还有 include,包含了使用 leveldb 需要引用的头文件:

编译使用

编译静态库

  1. 在源代码所在的文件夹(···/leveldb)里执行下面的命令,创建 build 文件夹 && 进入 build 文件夹:
    mkdir -p build && cd build
  2. 用 cmake 命令进行编译:
    cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build . // release 版本
    cmake -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . // debug 版本
  3. 编译完成后,可以在 build 目录下看到编译出的静态库 libleveldb.a

demo 代码

#include <iostream>
#include <string>
#include <leveldb/db.h>

using namespace std;
 
int main() {
  leveldb::DB* db;
  leveldb::Options opt;
  leveldb::Status status;
  
  // 打开 /workspace/leveldb/test/testdb 目录里的数据库,若不存在则新建
  opt.create_if_missing = true;
  status = leveldb::DB::Open(opt, "/workspace/leveldb/test/testdb",
                             &db);
  if (!status.ok()) {
    cout << "Failed to open db! " << status.ToString() << endl;
    return 0;
  }
 
  // 插入键值对 "test_key_A" - "test_value_a"
  string strKey = "test_key_A";
  string strVal = "test_value_a";
  status = db->Put(leveldb::WriteOptions(), strKey, strVal);
  if (!status.ok()) {
    cout << "Failed to exec put opt! " << status.ToString() << endl;
    delete db;
    return 0;
  }

  // 根据键 "test_key_A" 查询存储的值 "test_value_a"
  string strSavedVal;
  status = db->Get(leveldb::ReadOptions(), strKey, &strSavedVal);
  if (!status.ok()) {
    cout << "Failed to find val for key " << strKey << "! " 
         << status.ToString() << endl;
    delete db;
    return 0;
  }

  cout << "Got saved val[" << strSavedVal << "] for key " << strKey 
       << endl;

  // 删除键为 "test_key_A" 的键值对
  status = db->Delete(leveldb::WriteOptions(), strKey);
  if (!status.ok()) {
    cout << "Failed to del key-val for key " << strKey << "! " 
         << status.ToString() << endl;
    delete db;
    return 0;
  }      

  // 根据键 "test_key_A" 查询存储的值 "test_value_a"
  status = db->Get(leveldb::ReadOptions(), strKey, &strSavedVal);
  if (!status.ok()) {
    cout << "Failed to find val for key " << strKey << "! " 
         << status.ToString() << endl;
    delete db;
    return 0;
  }

  cout << "Got saved val[" << strSavedVal << "] for key " << strKey 
       << endl;
 
  delete db; 
  return 0;
}

demo 编译

  1. 把编译出的静态库 libleveldb.a 复制到 /usr/local/lib/
  2. 把 leveldb 源码中 include 文件夹里相关的头文件复制到 /usr/local/include/
  3. 保存 demo 代码为 demo.cpp,编译 demo:
    g++ demo.cpp -o demo -lleveldb -lpthread -std=c++11

demo 执行结果

用 Put 插入键值对 "test_key_A" - "test_value_a" 后,可以用 Get 接口,根据键 "test_key_A" 查询到存储的值 "test_value_a"。

用 Delete 删除键为 "test_key_A" 的键值对后,再用 Get 接口,根据键 "test_key_A" 查找键值对会返回失败,错误为 NotFound。

PS:如果是在 wsl 中使用 leveldb,需要升级到 wsl 2,在 wsl 1 中无法启动 leveldb。

数据库文件

按照 demo 实现,会在 /workspace/leveldb/test/testdb 目录下创建一个新的数据库:

数据库文件在上一篇文章里 【存储引擎】LevelDB 概述已经有部分提及。目录里 .ldb 后缀的文件就是上一篇文章里所说的持久化存储在硬盘中的 ssttable。.log 后缀的文件里存储的就是 WAL 日志(先写日志数据,再写用户数据,以保证用户数据的持久化):

目录里,还有 LOG 和 LOG.old 日志文件,记录的是数据库运行过程中的打印出来的日志,也就是通常意义上所说的日志(给人看的,用来调试、定位问题用的),每次重新打开数据库,都会创建新的 LOG,之前的 LOG 文件被重命名为 LOG.old:

目录中的 LOCK 是 leveldb 的文件锁。因为 leveldb 不允许多个进程同时访问数据库目录,所以每次打开数据库,leveldb 都会先获取 LOCK,获取成功才能下行流程,以此避免一个数据库被多个进程打开操作。

MANIFEST 文件包含的是 leveldb 的元信息,每次打开数据库,都会生成一个新的 MANIFEST 文件,新生成的 MANIFEST 文件有更大的版本号。

MANIFEST 文件可能会存在多个,CURRENT 文件记录的就是当前有效的 MANIFEST 的文件名:

参考

https://github.com/google/leveldb/blob/master/doc/impl.md

  • 24
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值