LevelDB之源码调试环境搭建

趁着前一篇LevelDB之LSM-Tree的文章中对LevelDB的技术好奇的热情尚未退却,决定一览LevelDB内部的实现,好歹对此类K-V键值存储系统有个代码级别的初步认识,果然还是有所收获的,比如对C++的学习、对数据结构的认识、Skiplist(跳表)、变长整数编码、memtable中的内存管理等等。

编译与调试环境搭建

windows8.1+vs2013

尝试过在windows环境下用VS2013进行编译调试,结果报一堆错误,经过各种修改,最终还是显示缺少源文件port/port_win.h,弃。期间参考了下面两篇帖子:

http://www.cnblogs.com/b-gao/p/leveldb_windows_vs2013_cpp.html
http://blog.csdn.net/flyfish1986/article/details/46806893

ubuntu16.04+eclipse

网上很多直接采用gdb的方式来调试,大神可以直接绕过。如果不满意于gdb的不友好,习惯IDE的,下面的方法也许有帮助。
用VMware安装ubuntu16.04,再在Ubuntu上安装了eclipse,网上教程很多,两行命令可以搞定:

1
2
$ sudo apt-get install eclipse #安装eclipse
$ sudo apt-get install eclipse-cdt #安装插件支持c/c++开发的cdt插件

安装完成后,尝试写一个helloWord(参考试着建一个C语言项目),如下:

1
2
3
4
5
6
#include <stdio.h>
#include <stdlib.h>
int main(){
printf("hello word");
return 0;
}

上述测试结束后,就可以开始levelDB的环境搭建了。
1.下载levelDB,可以直接clone。

1
$ git clone https://github.com/google/leveldb.git

2.导入项目,File -> New -> Makefile Project with Existing Code
3.选择源码目录,语言选择C语言。如果是 Linux 环境,则选中 Linux GCC,Mac 环境则选择 MacOSX GCC,Windows 下用 Eclipse 则可以选择 MinGW GCC。
4.编写test.cc文件调用levelDB的库,毕竟levelDB提供的就是一个库,将test.cc放到导入的工程里面,暂且放到db文件目录下。demo如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include "leveldb/db.h"
using namespace std;
using namespace leveldb;
int main() {
DB *db ;
Options op;
op.create_if_missing = true;
Status s = DB::Open(op,"/tmp/testdb",&db); ///tmp/testdb就是数据库文件
if(s.ok()){
cout << "创建成功11111" << endl;
s = db->Put(WriteOptions(),"abcd","1234");
if(s.ok()){
cout << "插入数据成功1111111" << endl;
string value;
s = db->Get(ReadOptions(),"abcd",&value);
if(s.ok()){
cout << "获取数据成功11111111,value:" << value << endl;
}
else{
cout << "获取数据失败" << endl;
}
}
else{
cout << "插入数据失败" << endl;
}
}
else{
cout << "创建数据库失败" << endl;
}
delete db;
cout<<"1212121212"<<endl;
return 0;
}

5.习惯了写java或者c的以为可以直接这样debug运行了,然后发现根本不行。从第二步我们知道该工程是用Makefile构建的,关于Makefile的作用可以参考什么是Makefile ,所以需要对test.cc进行预编译的过程才行。方法是修改Makfile:

1
2
3
4
5
6
7
8
9
# 修改编译参数
OPT ?= -g2
# 添加TESTS=
db/test \
# 添加下列到相应的地方
$(STATIC_OUTDIR)/test:db/test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS)
$(CXX) $(LDFLAGS) $(CXXFLAGS) db/test.cc $(STATIC_LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS)

  1. 项目levelDB ->右键build configuration ->build all,这个过程实际就是执行make的命令。
  2. 项目levelDB ->右键debug/run as ->local c/c++ application,bin选择test。
  3. 不出意外会输出test.cc中的数据。
    1
    2
    3
    4
    创建成功11111
    插入数据成功1111111
    获取数据成功11111111,value:1234
    1212121212

这样就OK了。

LevelDB工程目录简介

在看源码之前最好对LevelDB的原理有一个基本的认识,参看LevelDB之LSM-Tree.

docs

源码文档,建议从docs/index.html开始,里面介绍了LevelDB的一些重要组件,比如Snapshot、Slice、Comparators等,以及为了实现高性能所采取的一些方案实现,如Compression、Cache、Filters等等。
尤其是table_format.txt比较详细的解释了sstable的组织格式,在看说说他病了之前建议先看这个官方文档。网上的文章基本都是对该文件的解读。其他文档如impl.html、log_format.txt没有做仔细研读,就不说了。

db

memtable.h/cc :实现内存中的mentable,注意观察内部成员变量
skiplist.h :负责组织mentable中的kv键值对数据,跳表接口

table

block_builder.h/cc : 构造block,准备写入
block.h/cc :通过ReadBlock读取

table_builder.cc : 构造Table,Add()、Flush()、WriteBlock
table.cc : 定义了操作table的方法,InternalGet

util

arena.h/cc : 内存分配器,比较有意思,可以看看
coding.h/cc : 变长整数编码的实现,有意思
status.cc : 错误管理,返回状态之类的

include

slice.h : leveldb自己的封装字符串的类

其他目录文件可以参考源文件.