【存储引擎】LevelDB 编译使用

2022-04-18 00:00:00 数据库 代码 文件 编译 的是

代码获取

leveldb 的仓库地址为 https://github.com/google/leveldb。下载代码的时候好加上 --recurse-submodules 参数,直接把子模块的内容一并下载下来,因为编译会依赖子模块里的代码,缺少无法通过编译:
git clone --recurse-submodules https://github.com/google/leveldb.git

代码结构

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

编译使用

编译静态库

  • 在源代码所在的文件夹(···/leveldb)里执行下面的命令,创建 build 文件夹 && 进入 build 文件夹:

mkdir -p build && cd build
  • 用 cmake 命令进行编译:


// release 版本cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .  // debug 版本cmake -DCMAKE_BUILD_TYPE=Debug .. && cmake --build .
  1. 编译完成后,可以在 build 目录下看到编译出的静态库 libleveldb.a

demo 代码(建议横屏或用 PC 浏览)

#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 ; } // 插入键值对 "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 ; } // 根据键 "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 ; }
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 ; } // 根据键 "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 ; }
cout << "Got saved val[" << strSavedVal << "] for key " << strKey << endl;
delete db; return ;}

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

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://mp.weixin.qq.com/s/y_foxhvDzmLaUmpJ4HRr1Q

相关文章