LevelDb-用户接口

发布时间 2023-03-23 14:05:47作者: gatsby123

优缺点

  • This is not a SQL database. It does not have a relational data model, it does not support SQL queries, and it has no support for indexes.
  • Only a single process (possibly multi-threaded) can access a particular database at a time.
  • There is no client-server support builtin to the library. An application that needs such support will have to wrap their own server around the library.
    TODO:回头再来理解

用户接口

基本读写

打开关闭数据库

#include <cassert>
#include "leveldb/db.h"

leveldb::DB* db;
leveldb::Options options;
options.create_if_missing = true;
leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
assert(status.ok());
...
delete db;

读写

std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value);
if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1);

原子更新

#include "leveldb/write_batch.h"
...
std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
if (s.ok()) {
  leveldb::WriteBatch batch;
  batch.Delete(key1);
  batch.Put(key2, value);
  s = db->Write(leveldb::WriteOptions(), &batch);
}

delete key1和put key2在一个原子请求中,要么都不执行,要么都执行。

同步/异步写

leveldb::WriteOptions write_options;
write_options.sync = true;
db->Put(write_options, ...);

默认写是异步的,可以设置上述参数实现同步写。异步写速度快。

并发

一个数据库同时只能被一个进程使用。
一个进程中的DB对象是线程安全的,但是Iterator和WriteBatch这些对象不是。

迭代器

leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
  cout << it->key().ToString() << ": "  << it->value().ToString() << endl;
}
assert(it->status().ok());  // Check for any errors found during the scan
delete it;

遍历所有的数据。

for (it->Seek(start);
   it->Valid() && it->key().ToString() < limit;
   it->Next()) {
  ...
}

遍历[start, limit)区间的数据。

for (it->SeekToLast(); it->Valid(); it->Prev()) {
  ...
}

倒序遍历,效率不如正序遍历。

快照

    leveldb::ReadOptions read_options;
    read_options.snapshot = db->GetSnapshot();
    assert(status.ok());

    status = db->Put(leveldb::WriteOptions(), "key1", "value2");

    leveldb::Iterator* it = db->NewIterator(read_options);
    for (it->SeekToFirst(); it->Valid(); it->Next()) {
        std::cout << it->key().ToString() << ": "  << it->value().ToString() << std::endl;
    }

创建一个数据状态的可读快照。
如上所示,Put key1,value2之前,数据库里key1对应的是value1,在Put之前创建了快照对象,Put之后,用快照的key1的值依然是value1。

Slice

it->key() and it->value()返回的都是Slice实例,能和string互转。

leveldb::Slice s1 = "hello";

std::string str("world");
leveldb::Slice s2 = str;

自定义key比较器

默认的是按key的字典序进行排序。

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::Options options;
options.compression = leveldb::kNoCompression;
... leveldb::DB::Open(options, name, ...) ....

默认开启压缩,可用如上代码关闭。

缓存

#include "leveldb/cache.h"

leveldb::Options options;
options.block_cache = leveldb::NewLRUCache(100 * 1048576);  // 100MB cache
leveldb::DB* db;
leveldb::DB::Open(options, name, &db);
... use the db ...
delete db
delete options.block_cache;

设置缓存。

leveldb::ReadOptions options;
options.fill_cache = false;
leveldb::Iterator* it = db->NewIterator(options);
for (it->SeekToFirst(); it->Valid(); it->Next()) {
  ...
}

大量读请求的场景下,可以关闭文件缓存,防止在内存中缓存大量数据。

key设计

leveldb中连续的key会存在一起,根据这个特性,经常一起访问的数据key最好相似。

filename -> permission-bits, length, list of file_block_ids
file_block_id -> data

比如有这两类数据,filename最好以"/"开始,file_block_id最好以数据开始,这样查找filename对应的元信息时,不会扫描到file_block_id对应的数据。

布隆过滤

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;

设置布隆过滤器,取key的特征,判定key存在的概率,从而减少不必要读。

获取范围数据大小近似值

leveldb::Range ranges[2];
ranges[0] = leveldb::Range("a", "c");
ranges[1] = leveldb::Range("x", "z");
uint64_t sizes[2];
db->GetApproximateSizes(ranges, 2, sizes);

参考资料:
levedb doc