GitHub
https://github.com/google/leveldb
https://rawgit.com/google/leveldb/master/doc/index.html关于BigTable:
http://blog.csdn.net/opennaive/article/details/7532589
http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable关于Bloom过滤:
https://en.wikipedia.org/wiki/Bloom_filter
http://iluoxuan.iteye.com/blog/1718254 (推荐)分析
http://www.cnblogs.com/haippy/archive/2011/12/04/2276064.html
http://www.samecity.com/blog/Index.asp?SortID=12 (推荐)
LevelDB是Google开发的一个持久化的键值对儿数据库。Google Chrome等产品使用了它。它支持使用任意的字节数组作为key和value,可以使用用户自定义的比较函数对key进行排序。不仅允许单次执行get、put和delete操作,也允许批量的put和delete,双向迭代器,使用非常快速的Snappy算法进行压缩。
$ git clone https://github.com/google/leveldb.git
$ cd leveldb
$ make
编译完成后,生成了leveldb/out-static/libleveldb.a,这是一个静态链接库。而在leveldb/out-shared中是共享库。
测试程序:
#include <iostream>
#include <cassert>
#include "leveldb/db.h"
int main()
{
// open a db
leveldb::DB* db;
leveldb::Options options;
options.create_if_missing = true;
leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
assert(status.ok());
// read write
std::string key1("name");
std::string value1("hongjin"), valueReadBack;
leveldb::Status s = db->Put(leveldb::WriteOptions(), key1, value1);
if (s.ok())
{
s = db->Get(leveldb::ReadOptions(), key1, &valueReadBack);
std::cout << valueReadBack << std::endl;
}
// close db
delete db;
return 0;
}
编译方法:
$ g++ -Wall testleveldb.cpp -I leveldb/include/ leveldb/out-static/libleveldb.a -lpthread
上面的it->key() 和 it->value()的返回值类型是leveldb::Slice。Slice包含的数据成员只有两个:一个指向数据的指针和一个表示数据长度的整形变量。并没有使用std::string作为返回值,文档给出的理由是使用Slice可以避免数据的复制。
但是注意,在使用Slice时,Slice指向的数据必须由调用者保证数据的有效性,像下面这样的代码是有bug的:
leveldb::Slice slice;
if (...) {
std::string str = ...;
slice = str;
}
Use(slice); // bug
slice中的指针指向了str的数据,但是str被析构后,slice的指针就不能再使用了。
用于排序的默认Comparators是按照字典序进行排序(lexicographically)。用户可以自定义比较器。
下面这个自定义的比较器将key/value解析成数字进行排序:
class TwoPartComparator : public leveldb::Comparator {
public:
// Three-way comparison function:
// if a < b: negative result
// if a > b: positive result
// else: zero result
int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const {
int a1, a2, b1, b2;
ParseKey(a, &a1, &a2);
ParseKey(b, &b1, &b2);
if (a1 < b1) return -1;
if (a1 > b1) return +1;
if (a2 < b2) return -1;
if (a2 > b2) return +1;
return 0;
}
// Ignore the following methods for now:
const char* Name() const { return "TwoPartComparator"; }
void FindShortestSeparator(std::string*, const leveldb::Slice&) const { }
void FindShortSuccessor(std::string*) const { }
};
使用自定义比较器:
TwoPartComparator cmp;
leveldb::DB* db;
leveldb::Options options;
options.create_if_missing = true;
options.comparator = &cmp;
leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
...
leveldb将相邻的key放到同一个block中,而block是读写到持续存储介质的基本单位。block的默认大小是4096字节(未压缩)。
经常对数据库进行大块数据操作的程序可能会希望增加block size;但是对于每次仅仅读取一小块数据的程序来说,通常希望减小block size。
每个block在被写到存储介质上之前要被压缩。压缩是默认开启的,因为默认的压缩方法很快(>_<)。同时不会对不可压缩数据进行压缩。除非禁止压缩后bench的结果有提升,否则不要完全禁止压缩。
禁止压缩:
leveldb::Options options;
options.compression = leveldb::kNoCompression;
... leveldb::DB::Open(options, name, ...) ....
数据库的数据被存储在文件系统中的一组文件中,而每个文件存储了一系列的经过压缩的block。
可以设置options.cache
,来缓存(cache)经常被使用的block(缓存的是未压缩的数据)
#include "leveldb/cache.h"
leveldb::Options options;
options.cache = leveldb::NewLRUCache(100 * 1048576); // 100MB cache
leveldb::DB* db;
leveldb::DB::Open(options, name, &db);
... use the db ...
delete db
delete options.cache;
当读取一大块数据时,可以使用options.fill_cache = false
来禁止缓存被覆盖。
leveldb::ReadOptions options;
options.fill_cache = false;
leveldb::Iterator* it = db->NewIterator(options);
for (it->SeekToFirst(); it->Valid(); it->Next()) {
...
}
leveldb中的相邻key被存在一个block中,而多个block被存储在磁盘上的文件里面。这种组织形式会导致Get()操作会执行多次磁盘读操作。可以使用FilterPolicy(过滤器策略)来减少对磁盘的读操作。
leveldb::Options options;
options.filter_policy = NewBloomFilterPolicy(10);
leveldb::DB* db;
leveldb::DB::Open(options, "/tmp/testdb", &db);
... use the database ...
delete db;
delete options.filter_policy;
关于Bloom过滤,请参考下面的文章
https://en.wikipedia.org/wiki/Bloom_filter
http://iluoxuan.iteye.com/blog/1718254 (推荐)
此外,如果你自定义了比较器,请保证你使用的过滤策略与比较器是兼容的。
leveldb存储在磁盘上的数据都有一个校验和。
GetApproximateSizes
方法可以用来得到key range在所文件系统中占用的空间大小。
leveldb::Range ranges[2];
ranges[0] = leveldb::Range("a", "c");
ranges[1] = leveldb::Range("x", "z");
uint64_t sizes[2];
leveldb::Status s = db->GetApproximateSizes(ranges, 2, sizes);
sizes[0] 是 key range [a..c) 所占用的大小。
sizes[1] 是 key range [x..z) 所占用的大小。