0%

LevelDB的编译,运行和调试

这是LevelDB 代码阅读系列博客的第一篇,更多内容见

levelDB代码阅读笔记

主要内容

由于研究生工作需要用到LevelDB相关的知识,所以拜读一下谷歌大牛所写的LevelDB。对LevelDB和LSM Tree的介绍这里不进行介绍,后期有时间回来填坑。

想阅读源码,首先要先把代码编译通过并运行起来,这样我们才能在后期愉快的调试代码。本篇文章主要说明如何跑起LevelDB。

本系列文档基于LevelDB的1.22版本。

本篇主要分为三个部分:

  • 编译并运行LevelDB
  • LevelDB的Benchmark
  • LevelDB模块的单元测试

编译并运行LevelDB

首先我们先去Github上把代码Clone下来。地址在这。

levelDB使用cmake构建。在工程README中已经给出了编译方法。

1
2
mkdir -p build && cd build
cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .

这采用了cmake外部编译的方法,好处就是编译生成的文件都在一个文件夹中,不会影响代码部分。

和内存数据库Redis不同。Redis编译完会有两个可执行文件,即CLient和Server。Server相当与一个一直运行在系统内的应用。而Client则是去连接它。而LevelDB编译完则是生成一个静态链接库。即build文件夹中的libleveldb.a文件。其类似用txt文件维护数据库而不是发送请求给server。

那我们要如何使用这个库呢?首先写一个简单的cpp程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <cassert>
#include <cstdlib>
#include <string>
#include "leveldb/db.h" // TAG1

int main() {
leveldb::DB *db;
leveldb::Options options;
options.create_if_missing = true;
leveldb::Status status = leveldb::DB::Open(options,"./leveldb",&db);
std::cout << status.ToString() << std::endl;
assert(status.ok());

std::string key = "name";
std::string value;
leveldb::Status s = db->Put(leveldb::WriteOptions(), key, "henry");
s = db->Get(leveldb::ReadOptions(), key, &value);
std::cout << "get a key: " << key << ", value is: " << value << std::endl;
delete db;

return 0;
}

这个程序非常简单,就是创建并打开数据库,插入一条KV对。我们可以看到我们include了一个用户程序 db.h。这个程序来源于levelDB中的include文件夹中。为了让其可用,我们要在cmake中加上其路径。

1
include_directories(../leveldb-master/include)

同时我们需要添加leveldb静态库和链接

1
2
link_directories ("../leveldb-master/build/")
link_libraries(leveldb pthread)

cmake文件如下:

1
2
3
4
5
6
7
8
9
10
cmake_minimum_required(VERSION 3.09)
project(leveldb_test)

set(CMAKE_CXX_STANDARD 11)

include_directories(../leveldb-master/include)
aux_source_directory( ./src DIR_SRCS )
link_directories ("../leveldb-master/build/")
link_libraries(leveldb pthread)
add_executable(leveldb_test main.cpp)

只需要 mkdir build && cd build && cmake .. && make. 就可以跑起了。会打印出KV对。

levelDB的benchmark

LevelDB自带了三种benchmark程序,即levelDB下benchmark目录中的三个文件。这里主要分析db_bench这个测试。(由于这个测试不需要其他依赖,而db_bench_sqlite3和db_bench_tree_db都需要额外的依赖。)

这个测试主要测试了顺序写(with/without async)。覆盖写,随机写(with/without sync)。顺序/随机删,顺序/逆序/随机读。在编译完成后就会在build目录下生成db_bench可执行文件。直接跑就可以,结果如下图。

结果

可以看到,在关闭sync时,顺序写和随机写的性能差距(4 vs 9)相比于顺序读和随机读(0.1 vs 2-5)来说小了很多。这就可以看到LSM Tree的优秀写性能。同时看到,开启sync时,由于产生了很多小I/O,导致性能的大量损失(10000X)。以及compaction时的性能损失。

不过我感受最深刻的仍然是那个函数指针的使用。反正给我是完全想不到。

LevelDB的单元测试

LevelDB是Google两位大牛写的项目,自然使用了Google的测试框架。对谷歌测试框架相关内容我后期会单开坑来写,这里不进行表述。

在leveldb中的测试框架的使用很简单。只需要创建一个测试类,然后调用TEST macro(参数为测试类和测试名称)。框架也提供了一系列宏。这部分要等后期做修改的时候再进行验证。只需要再cmake文件中加入 leveldb_test就可以添加相应测试了。