「LevelDB从前门到后门」之蜻蜓点水篇

发布一下 0 0

因为自己对数据库比较感兴趣,所以听说LevelDB时间比较早,但因种种原因一直没有写过相关的文章。这段时间不忙,准备做一个系列相关的主题。

我们知道,现在主流的数据库有从关系型数据库逐渐往分布式数据库迁移的趋势,比如大名鼎鼎的TiDB和PolarDB。而TiDB底层存储引擎使用的是RocksDB,RocksDB则使用了LevelDB(这就是开源的力量,相互借鉴)。从这方面看,LevelDB的性能可见一斑。

本篇文章主要介绍LevelDB的一些特点和基础概念。LevelDB是LSM (Log Structured Merge)算法的实现。里面涉及大量关于文件系统读写技术的真实案例。我们可以从中收货很多重要信息。

当前官方的源码地址:C++版本,另外还有两个移植版本分别是GoLevelDB和JavaLevelDB。工程从结构和文件命名上基本保持了跟C++版本的一致。各位看官可以根据自己的喜好各取所需。不过建议还是从C++官方版本看起。有位老哥之前做过一版注释,不过可能是基于main主分支为母版,但是并不影响代码阅读。我使用当前版本为1.22。

我自己用的是Mac电脑,主要开发语言是Java。对于C++的使用,更倾向于使用centos系统。所以我平时都是自建虚拟机,然后通过VSCode插件Remote Development“远程”控制来完成项目编译和调试工作。

获取源代码:

git clone --recurse-submodules https://github.com/google/leveldb.git

构建:

mkdir -p build && cd build cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .

注:编译完是没有out-sharedout-static这两个文件夹 。

头文件介绍:

leveldb 对外暴露的接口都在 include/*.h 中,用户不应该依赖任何其它目录下的头文件,这些内部 API 可能会在没有警告的情况下被改变。

  • include/leveldb/db.h:主要的 DB 接口,从这开始。
  • include/leveldb/options.h: 控制数据库的行为,也控制当个读和写的行为。
  • include/leveldb/comparator.h: 比较函数的抽象。如果你只想对 key 逐字节比较,可以直接使用默认的比较器。如果你想要自定义排序(例如处理不同的字符编码、解码等),可以实现自己的比较器。
  • include/leveldb/iterator.h:迭代数据的接口,你可以从一个 DB 对象获取到一个迭代器。
  • include/leveldb/write_batch.h:原子地将多个操作应用到数据库。
  • include/leveldb/slice.h:类似 string,维护着指向字节数组的指针和对应的长度。
  • include/leveldb/status.h:许多公共接口都会返回 Status,用于报告成功或各种错误。
  • include/leveldb/env.h:操作系统环境的抽象,该接口的 posix 实现位于 util/env_posix.cc 中.
  • include/leveldb/table.h, include/leveldb/table_builder.h:底层的模块,大多数用户可能不会直接用到。

我们可以编写一段代码,来进行简单的测试:

#include <assert.h>#include <string.h>#include <leveldb/db.h>#include <iostream>using namespace leveldb;int main(){    leveldb::DB* db;    leveldb::Options options;    options.create_if_missing = true;    // 打开一个数据库,不存在就创建    leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);    assert(status.ok());   // 插入一个键值对    status = db->Put(leveldb::WriteOptions(), "hello", "LevelDB");    assert(status.ok());   // 读取键值对    std::string value;    status = db->Get(leveldb::ReadOptions(), "hello", &value);    assert(status.ok());    std::cout << value << std::endl;    delete db;    return 0;}

编译命令:

g++   test_db.cc  -o test_db  -L /usr/local/lib64  -lleveldb -lpthread -D_GLIBCXX_USE_CXX11_ABI=0  -std=c++11

指定静态库路径:-L /usr/local/lib64 ,静态库名称:-lleveldb D_GLIBCXX_USE_CXX11_ABI:GCC版本1.5前后接口兼容性问题闭坑参数。

执行test_db文件后,进入/tmp/testdb目录下,看到以下几个文件:

[root@localhost testdb]# cd /tmp/testdb/[root@localhost testdb]# lltotal 16-rw-r--r-- 1 root root 34 Oct 14 03:45 000003.log-rw-r--r-- 1 root root 16 Oct 14 03:45 CURRENT-rw-r--r-- 1 root root  0 Oct 14 03:45 LOCK-rw-r--r-- 1 root root 77 Oct 14 03:45 LOG-rw-r--r-- 1 root root 50 Oct 14 03:45 MANIFEST-000002[root@localhost testdb]# pwd/tmp/testdb[root@localhost testdb]#

喜欢用Java的同学可以引入对应的文件包同样可以完成上面的操作,POM文件:

<dependency>      <groupId>org.iq80.leveldb</groupId>      <artifactId>leveldb-api</artifactId>      <version>0.12</version></dependency><dependency>      <groupId>org.iq80.leveldb</groupId>      <artifactId>leveldb</artifactId>      <version>0.12</version></dependency>

Java代码:

import org.iq80.leveldb.DB;import org.iq80.leveldb.DBFactory;import org.iq80.leveldb.Options;import org.iq80.leveldb.impl.Iq80DBFactory;import java.io.File;import java.io.IOException;public class TestLevelDB {    public static void main(String[] args) throws IOException {        DBFactory factory = new Iq80DBFactory();        Options options = new Options();        options.createIfMissing(true);        DB db = factory.open(new File("/tmp/testdb"), options);        db.put(Iq80DBFactory.bytes("leveldb") , Iq80DBFactory.bytes("It is leveldb."));        byte[] bytes = db.get(Iq80DBFactory.bytes("leveldb"));        System.out.println(Iq80DBFactory.asString(bytes));    }}

总结

通过以上内容,我大概了解到了一些leveldb的特性和不足,并且做了简单的上机实验。更多功能测试,可以参考官方文档。后续我们将系统阐述leveldb各模块实现以及模块之间如何协作高效完成文件的读写能力。


资料参考:

基本概念 — leveldb-handbook 文档

Leveldb 基本介绍和使用指南 - 掘金 (juejin.cn)

头条居然不支持外链!

版权声明:内容来源于互联网和用户投稿 如有侵权请联系删除

本文地址:http://0561fc.cn/182776.html