1. leveldb简介
LevelDb是google的工程师Jeff Dean和Sanjay Ghemawat这两位大神级别的工程师发起的开源项目,这两位是Google重量级的工程师,为数甚少的Google Fellow之二。Jeff Dean其人是Google大规模分布式平台Bigtable和MapReduce主要设计和实现者,Sanjay Ghemawat是Google大规模分布式平台GFS,Bigtable和MapReduce主要设计和实现工程师。Google大数据的三驾马车论文参考:GFS论文阅读、Mapreduce论文阅读、BigTable论文阅读。
LevelDb是能够处理十亿级别规模Key-Value型数据持久性存储的嵌入式数据库,内部没有设计成C/S网络结构,必须和服务部署在同一台服务器。levelDB其实也是一个C++程序库,是可以可以被编译和链接到我们自己的程序中的库,而不是像Redis那样是一个数据库服务器。LevelDb本质上是一个存储引擎,即一套存储系统以及在这套存储系统上提供的一些操作接口。LevelDb有如下一些特点:
- 首先,LevelDb是一个持久化存储的KV系统,和Redis这种内存型的KV系统不同,redis是一个内存数据库,它是将KV写入内存中,而LevelDb不会像Redis一样狂吃内存,而是将大部分数据存储到磁盘上。从某种意义上说,redis是非持久化的,而levelDB是实时持久化的。
- 其次,LevelDb在存储数据时,是根据记录的key值有序存储的,就是说相邻的key值在存储文件中是依次顺序存储的,而应用可以自定义key大小比较函数,LevleDb会按照用户定义的比较函数依序存储这些记录。
- 再次,像大多数KV系统一样,LevelDb的操作接口很简单,基本操作包括写记录,读记录以及删除记录。也支持针对多条操作的原子批量操作。
- 另外,LevelDb支持数据快照(snapshot)功能,使得读取操作不受写操作影响,可以在读操作过程中始终看到一致的数据。
- 除此外,LevelDb还支持数据压缩等操作,这对于减小存储空间以及增快IO效率都有直接的帮助。
2. hello world
2.1 leveldb源码下载和编译
git clone --recurse-submodules https://github.com/google/leveldb.git
mkdir -p build
cd build
cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .
编译好之后,在build目录会生成libleveldb.a静态库文件,在使用时,我们只需要把源码中的头文件目录leveldb/加入到我们工程的include目录,并且把编译好的静态库文件加入到我们工程的lib目录即可,这里我使用cmake来编译工程,具体的工程配置和编译参考GoogleTest入门,在编译过程中注意需要链接pthread线程库。
2.2 hello world程序
接下来我们来分析一下leveldb的hello world程序。
#include <iostream>
#include <cassert>
#include "leveldb/db.h"
#include "leveldb/write_batch.h"
int main()
{
// Open a database.
leveldb::DB* db;
leveldb::Options opts;
opts.create_if_missing = true;
leveldb::Status status = leveldb::DB::Open(opts, "./testdb", &db);
assert(status.ok());
// Write data.
status = db->Put(leveldb::WriteOptions(), "name", "jinhelin");
assert(status.ok());
// Read data.
std::string val;
status = db->Get(leveldb::ReadOptions(), "name", &val);
assert(status.ok());
std::cout << val << std::endl;
// Batch atomic write.
leveldb::WriteBatch batch;
batch.Delete("name");
batch.Put("name0", "jinhelin0");
batch.Put("name1", "jinhelin1");
batch.Put("name2", "jinhelin2");
batch.Put("name3", "jinhelin3");
batch.Put("name4", "jinhelin4");
batch.Put("name5", "jinhelin5");
batch.Put("name6", "jinhelin6");
batch.Put("name7", "jinhelin7");
batch.Put("name8", "jinhelin8");
batch.Put("name9", "jinhelin9");
status = db->Write(leveldb::WriteOptions(), &batch);
assert(status.ok());
// Scan database.
leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
std::cout << it->key().ToString() << ": " <<
it->value().ToString() << std::endl;
}
assert(it->status().ok());
// Range scan, example: [name3, name8)
for (it->Seek("name3");
it->Valid() && it->key().ToString() < "name8";
it->Next()) {
std::cout << it->key().ToString() << ": " <<
it->value().ToString() << std::endl;
}
// Close a database.
delete db;
}
这个例子简单介绍了 LevelDB 的基本用法,包括:
(1)打开数据库:
leveldb::DB* db;
leveldb::Options opts;
opts.create_if_missing = true;
leveldb::Status status = leveldb::DB::Open(opts, "./testdb", &db);
assert(status.ok());
leveldb::DB::Open()打开数据库需要三个参数:
- leveldb::Options :控制 DB 行为的一些参数。在这里 create_if_missing 为 true 表示如果数据库./testdb 存在就直接打开,不存在就创建。
- ./testdb :LevelDB 数据库的根目录。一个 LevelDB 数据库存放在一个目录下。
- &db :用来返回一个 LevelDB 实例。
- leveldb::Status :封装了 leveldb 接口返回的详细信息。
(2)写入一条数据:
status = db->Put(leveldb::WriteOptions(), "name", "jinhelin");
assert(status.ok());
db->put 接口的三个参数:
- leveldb::WriteOptions :目前里面只有一个 sync 成员。表示写完 WAL 后是否需要 flush。
- 另外两个参数分别是本次写入数据的 Key 和 Value。
(3)读取一条数据:
std::string val;
status = db->Get(leveldb::ReadOptions(), "name", &val);
assert(status.ok());
std::cout << val << std::endl;
Get 接口和 Put 接口比较像,除了 leveldb::ReadOptions 参数是用来控制读操作的.
(4)批量原子操作:
leveldb::WriteBatch batch;
batch.Delete("name");
batch.Put("name0", "jinhelin0");
batch.Put("name1", "jinhelin1");
batch.Put("name2", "jinhelin2");
batch.Put("name3", "jinhelin3");
batch.Put("name4", "jinhelin4");
batch.Put("name5", "jinhelin5");
batch.Put("name6", "jinhelin6");
batch.Put("name7", "jinhelin7");
batch.Put("name8", "jinhelin8");
batch.Put("name9", "jinhelin9");
status = db->Write(leveldb::WriteOptions(), &batch);
assert(status.ok());
LevelDB 的 Write 接口支持原子地修改多条数据,主要参数是 leveldb::WriteBatch 。
(5)数据库的遍历和范围查找:
leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
std::cout << it->key().ToString() << ": " <<
it->value().ToString() << std::endl;
}
assert(it->status().ok());
// Range scan, example: [name3, name8)
for (it->Seek("name3");
it->Valid() && it->key().ToString() < "name8";
it->Next()) {
std::cout << it->key().ToString() << ": " <<
it->value().ToString() << std::endl;
}
LevelDB 通过提供 leveldb::Iterator 来实现范围查找。
(6)关闭数据库:
delete db;
关闭数据库时需要删除掉创建的数据库实例,让其调用析构函数处理一些收尾工作。