天天看點

LevelDB 介紹介紹編譯使用SliceComparatorsBlock sizeCompressionCacheFiltersChecksumsApproximate Sizes

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) 所占用的大小。