当前位置 博文首页 > 周小伦:LevelDB学习笔记 (1):初识LevelDB

    周小伦:LevelDB学习笔记 (1):初识LevelDB

    作者:周小伦 时间:2021-07-03 18:25

    LevelDB学习笔记 (1):初识LevelDB

    1. 写在前面

    1.1 什么是levelDB

    LevelDB就是一个由Google开源的高效的单机Key/Value存储系统,该存储系统提供了Key到Value的有序映射。

    地址: https://github.com/google/leveldb

    中文文档: https://kevins.pro/leveldb_chinese_doc.html

    1.2 为什么要学levelDB

    学习源码算是一种很好的学习方式,准备精读几个经典的开源代码,那学习levelDB的原因主要有下

    1. 谷歌开源,代码质量非常高,而且只有1w行(去掉测试代码)不是过于复杂
    2. 现在主流的kv存储系统有很多基于levelDB或者借鉴了levelDB的思想

    2. Linux/Mac下的编译运行

    1. Quick start

    下面来自于官方文档

    mkdir -p build && cd build
    cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build . #release版本
    cmake -DCMAKE_BUILD_TYPE=Debug .. && cmake --build .	 #debug版本
    

    在类linux系统下编译非常简单,上面两行就可以了

    不过这里编译可能会遇到一些问题

    比如我遇到了上面的问题,这是因为在third_party这个文件夹内缺少googletest和benchmark。

    这里手动下载一下这两个文件把这个目录下的空文件替换了就好了。然后在执行cmake即可

    benchmark

    googletest

    上面是对应的github地址,直接点进去git clone就行,当然最近的github好像git clone总是出问题,我是直接下载zip然后替换过去的。

    2. Quick test

    新建一个文件夹叫my_test。添加一个新的cpp文件run_test.cpp

    随后修改CMakeList.txt里添加关于新测试文件的规则。就可以了

    我们准备写一个运行的测试文件,来创建一个db,随后进行添加和删除数据

    #include <iostream>
    #include <cassert>
    #include "leveldb/db.h"
    #include "leveldb/write_batch.h"
    int main()
    {
        // Open a database.
        leveldb::DB* db;
        leveldb::Options opts;
        opts.create_if_missing = true;
        leveldb::Status status = leveldb::DB::Open(opts, "../tmp/testdb", &db);
        assert(status.ok());
    
        // Write data.
        status = db->Put(leveldb::WriteOptions(), "name", "zxl");
        assert(status.ok());
    
        // Read data.
        std::string val;
        status = db->Get(leveldb::ReadOptions(), "name", &val);
        assert(status.ok());
        std::cout << val << std::endl;
       }
    

    上面的代码就是我们在tmp文件下创建一个testdb。然后写入一条数据 key = "name" value = "zxl"。这个时候我们执行get操作就会得到我们刚才写入的值

    3. LevelDB的基本操作

    关于数据库的创建以及读写操作我们上面以及提过了

    3.1 原子更新-Atomic Updates

    下面的例子来自官方文档

    考虑下面这一种情况,假如进程在Put key2 和 Delete key1两个操作之间结束,那么key1和key2这两个不同的键值将存储相同的值,这和我们的意愿相违背。为了避免这种情况的出现,leveldb利用WriteBatch来进行具有原子性的更新。

    td::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);
    }
    

    WriteBatch对象保存对数据库进行的一系列操作,然后在这一批次中按照顺序执行这些操作。

    注意:这里先进行Delete操作,然后再进行Put操作。

    WriteBatch类除了原子性的优势外,也可以用于通过将大量个体变动放置在同一批次中而加速批量更新。

    3.2 迭代 - Iteration

    下面的例子来自官方文档

    下面的例子演示了如何从数据库中成对的打印键值:

    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;
    

    下面的修改后的例子演示了如何仅获取[start, limit)范围内的键;

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

    还可以通过相反的顺序进行处理。(警告:反向迭代比正向迭代慢一些)

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

    好下面结合迭代和刚才学到的原子更新自己写一个测试文件

    1. 顺序全体迭代
     // test Atomic Updates
        leveldb::WriteBatch batch;
        batch.Delete("name");
        batch.Put("name0", "zxl0");
        batch.Put("name1", "zxl1");
        batch.Put("name2", "zxl2");
        status = db->Write(leveldb::WriteOptions(), &batch);
        assert(status.ok());
        //Scan database.
        leveldb::Iterator *it = db->NewIterator(leveldb::ReadOptions());
        for (it->SeekToFirst(); it->Valid(); it->Next()) {
            std::cout << it->key().ToString() << ": " <<
                      it->value().ToString() << std::endl;
        }
        assert(it->status().ok());
    

    这段代码就会输出我们写入的三个k-v对

    2. range 迭代

      for (it->Seek("name1"); it->Valid() && it->key().ToString() < "name2"; it->Next()) {
        std::cout << it->key().ToString() << ": " <<
                  it->value().ToString() << std::endl;
      }
      delete it;
    

    只会输出key值位于[name1,"name2")之间的value也就只输出了key=name1的value

    当然还有其他的基本操作,但是这里我们展示了增删改查已经足够了.后面会在梳理具体实现的时候在增加新的测试文件

    bkbky
    下一篇:没有了