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 ;
}
編譯方法:
Slice
上面的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
用于排序的預設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 -;
if (a1 > b1) return +;
if (a2 < b2) return -;
if (a2 > b2) return +;
return ;
}
// 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);
...
Block size
leveldb将相鄰的key放到同一個block中,而block是讀寫到持續存儲媒體的基本機關。block的預設大小是4096位元組(未壓縮)。
經常對資料庫進行大塊資料操作的程式可能會希望增加block size;但是對于每次僅僅讀取一小塊資料的程式來說,通常希望減小block size。
Compression
每個block在被寫到存儲媒體上之前要被壓縮。壓縮是預設開啟的,因為預設的壓縮方法很快(>_<)。同時不會對不可壓縮資料進行壓縮。除非禁止壓縮後bench的結果有提升,否則不要完全禁止壓縮。
禁止壓縮:
leveldb::Options options;
options.compression = leveldb::kNoCompression;
... leveldb::DB::Open(options, name, ...) ....
Cache
資料庫的資料被存儲在檔案系統中的一組檔案中,而每個檔案存儲了一系列的經過壓縮的block。
可以設定
options.cache
,來緩存(cache)經常被使用的block(緩存的是未壓縮的資料)
#include "leveldb/cache.h"
leveldb::Options options;
options.cache = leveldb::NewLRUCache( * ); // 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()) {
...
}
Filters
leveldb中的相鄰key被存在一個block中,而多個block被存儲在磁盤上的檔案裡面。這種組織形式會導緻Get()操作會執行多次磁盤讀操作。可以使用FilterPolicy(過濾器政策)來減少對磁盤的讀操作。
leveldb::Options options;
options.filter_policy = NewBloomFilterPolicy();
leveldb::DB* db;
leveldb::DB::Open(options, "/tmp/testdb", &db);
... use the database ...
delete db;
delete options.filter_policy;
Bloom Filter
關于Bloom過濾,請參考下面的文章
https://en.wikipedia.org/wiki/Bloom_filter
http://iluoxuan.iteye.com/blog/1718254 (推薦)
此外,如果你自定義了比較器,請保證你使用的過濾政策與比較器是相容的。
Checksums
leveldb存儲在磁盤上的資料都有一個校驗和。
- ReadOptions::verify_checksums 設定為true來開啟從檔案系統讀資料時進行校驗,預設是關閉的。
- Options::paranoid_checks 打開資料庫之前,将此項設定為true,會讓leveldb檢測到一個 internal corruption時raise an error。預設是關閉的,這樣做可是在一部分存儲出錯時讓資料庫仍然可用。
Approximate Sizes
GetApproximateSizes
方法可以用來得到key range在所檔案系統中占用的空間大小。
leveldb::Range ranges[];
ranges[] = leveldb::Range("a", "c");
ranges[] = leveldb::Range("x", "z");
uint64_t sizes[];
leveldb::Status s = db->GetApproximateSizes(ranges, , sizes);
sizes[0] 是 key range [a..c) 所占用的大小。
sizes[1] 是 key range [x..z) 所占用的大小。