02-leveldb入门—从0开始编译和使用leveldb

Posted anda0109

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了02-leveldb入门—从0开始编译和使用leveldb相关的知识,希望对你有一定的参考价值。

01-leveldb编译和使用

在github下载开源代码:git clone --recurse-submodules https://github.com/google/leveldb.git

进入项目根目录,执行以下命令:
mkdir -p build && cd build

cmake -DCMAKE_BUILD_TYPE=Release … && cmake --build .

以上命令运行之后会编译全部代码包括测试程序,windows下编译生成文件在leveldb/build/Release下。同时在leveldb/build目录下可以打开leveldb.sln工程文件,在visual studio中查看工程代码。

一个简单的应用leveldb实现数据写入、查询的例子,依照国际惯例,还是从“hello world”开始:

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

int main()

    //open database
    leveldb::DB* db;
    leveldb::Options options;
    options.create_if_missing = true;
    leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
    if (!status.ok()) 
    
        std::cout << "open db failed" << std::endl;
        return -1;
    
    
    std::string key = "first_key";
    std::string value = "hello world";
    leveldb::Status s = db->Put(leveldb::WriteOptions(), key, value);
    value.clear();
    if (s.ok())
    
        s = db->Get(leveldb::ReadOptions(), key, &value);
    
    if(s.ok())
    
        std::out << value << std::endl;
    
    
    //close database
    delete db;
    return 0;

运行结果:Hello world

02-leveldb的基本操作

1-打开数据库

打开leveldb数据库需要指定一个文件系统目录,数据库的所有文件都存放在该目录下。

#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());
...

2-读写数据

leveldb提供了Get、Put、Delete方法来进行数据的查询、写入和删除操作。没有提供更新接口,直接使用Put接口写入一个新的value即会覆盖之前的值。

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);

3-原子更新

如果某个业务的处理需要连续写入多个数据,这些数据会依次写入。如果中途宕机则可能导致部分写入成功,部分失败,从而影响业务的完整性。使用WriteBatch批处理功能,则会保证一个WriteBatch的所有操作要么全部成功,要么全部失败,即保证原子性。

#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);

4-同步写

leveldb提供了同步写方式,即每次写入数据后强制刷盘。这种方式能保证机器宕机情况下数据的落盘安全,但会极大地降低数据库写入性能。在实际使用中我们往往不会采用同步写的方式,即允许极端情况下的一些数据丢失,而保证其使用的性能。

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

5-迭代

以下代码用于迭代数据库的所有数据。

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;

以下代码用于迭代指定范围内的数据。

leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
for (it->Seek(start);
   it->Valid() && it->key().ToString() < limit;
   it->Next()) 
  ...

assert(it->status().ok());  // Check for any errors found during the scan
delete it;

以下代码用于倒序迭代所有的数据。

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

5-快照

快照提供了整个存储数据状态的一致性只读视图。ReadOptions::snapshot如果不为空,则会在指定的数据库状态版本上进行读取。如果ReadOptions::snapshot为空,则会在当前状态隐式地创建一个快照,即在当前快照上进行读取。

leveldb::ReadOptions options;
options.snapshot = db->GetSnapshot();
... apply some updates to db ...
leveldb::Iterator* iter = db->NewIterator(options);
... read using iter to view the state when the snapshot was created ...
delete iter;
db->ReleaseSnapshot(options.snapshot);

需要注意当快照不再使用时,需要调用ReleaseSnapshot释放快照,以免需要维护其状态而占用资源。

6-缓存

当block_cache不为空时,最常使用的数据块会进行缓存,这些块是未进行压缩的。使用缓存有利用优化查询性能,尤其是对于频繁查询相同数据的场景。

#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;

7-布隆过滤器

leveldb的查询可能要进行多次磁io,为了减少磁盘IO,提升查询性能,可以开户布隆过滤器。

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;

03-leveldb参数介绍

leveldb的主要参数通过Options设置,其在options.h文件中定义。下面就介绍下其主要参数及含义:

  • const Comparator* comparator;比较函数,主要用于key的大小比较。如果传入NULL则使用默认字节序进行比较。
  • bool create_if_missing = false;如果数据库不存在,则创建。
  • bool error_if_exists = false;如果数据库存在,返回错误。
  • bool paranoid_checks = false;如果开启,则在读取数据时进行严格的检查,若发现数据损坏,则立即结束。
  • Env* env;用户定义环境,用于文件读写、后台线程等,默认Env::Default()。
  • Logger* info_log;日志对象指针,如果传入为空,则使用默认定义的日志对象。
  • size_t write_buffer_size = 4 * 1024 * 1024;写缓冲区的大小,对应于memtable的空间大小,会影响并发写入性能,原理部分会进行详细说明。
  • int max_open_files = 1000;最大打开文件数,也会关系到tablecache的大小.
  • Cache* block_cache = nullptr;data block缓存,如果为nullptr则数据块不进行缓存。
  • size_t block_size = 4 * 1024;数据块的大小,默认为4k,一般不需修改。
  • int block_restart_interval = 16;重启点的间隔,即每16个key进行前缀压缩。
  • size_t max_file_size = 2 * 1024 * 1024;文件最大大小,根据存储数据大小进行调整。
  • CompressionType compression = kSnappyCompression;是否对数进行压缩。
  • const FilterPolicy* filter_policy = nullptr;布隆过滤器,使用布隆过滤器有利于提高查询效率。

以上只是对参数做简单介绍,对于初学者,如果只是运行个demo,则不用太多理解即可使用。若是在实际项目中使用,则需要根据情况进行设置,以保证其性能等需求。

如果希望对参数充分理解,则需要对leveldb的原理进行深入了解,甚至阅读和分析源码。这对于想要充分利用和改造leveldb是必需的。下一章将会详细讲解leveldb的原理,随着对leveldb原理的深入,这些参数的具体含义也都会迎刃而解。

以上是关于02-leveldb入门—从0开始编译和使用leveldb的主要内容,如果未能解决你的问题,请参考以下文章

08-leveldb性能优化

08-leveldb性能优化

08-leveldb性能优化

英特尔 Fortran 向量化:向量循环成本高于标量

从植物大战僵尸开始一步一步带你入门逆向工程,

从零开始学深度学习编译器七,万字长文入门TVM Pass