ToplingDB高性能 K-V 数据库引擎

联合创作 · 2023-10-01 02:31

ToplingDB 是由北京拓扑岭科技有限公司出品的一款开源数据库引擎。Fock 自 RocksDB 并进行内核改造,以独有的“可检索内存压缩”和“分布式 compact”等技术手段,大大提升数据库引擎的性能和可用性。

部署安装 ToplingDB

  1. 服务器环境

    操作系统: CentOS Linux release 8.4.2105

    g++版本: g++ (GCC) 8.4.1 20200928 (Red Hat 8.4.1-1)

  2. 安装相关依赖

    ToplingDB 基于 RocksDB 构建,我们需要用到压缩库 snappy 和命令行参数解析工具 gflags 。除此之外,在编译的过程中,还需要用到 libaio 的开发包。

    • 安装 snappy :

      1
      
      sudo yum install snappy snappy-devel
      
    • 安装 gflags :

      • 对于 CentOS 8:

        1
        
        sudo dnf --enablerepo=powertools install gflags-devel
        
      • 对于 CentOS 7(需要 EPEL ):

        1
        
        sudo yum install gflags-devel
        
    • 安装 libaio-devel :

      1
      
      sudo yum install libaio-devel
      
  3. 安装 ToplingDB

    • 获取项目源代码:

      1
      2
      
      cd ~
      git clone https://github.com/topling/toplingdb.git
      
    • 更新依赖的子项目:

      1
      2
      
      cd toplingdb
      git submodule update --init --recursive
      
    • 编译安装动态库:

      1
      2
      
      make shared_lib
      sudo make install
      
    • 设置环境变量:

      除了 librocksdb.so 之外,我们还会用到 topling-zip 编译生成的 libterark-zbs-r.so 等动态库。在刚才的 make 过程中, topling-zip 已被克隆到 toplingdb/sideplugin 目录下,它编译得到的动态库位于 topling-zip/build/Linux-x86_64-g++-8.4-bmi2-1/lib_shared 

      打开文件 ~/.bashrc ,在文件的末尾增加下列两行:

      1
      2
      
      export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
      export LD_LIBRARY_PATH=~/toplingdb/sideplugin/topling-zip/build/Linux-x86_64-g++-8.4-bmi2-1/lib_shared:$LD_LIBRARY_PATH
      

      保存后,执行以下命令,更新我们的设置:

      1
      
      source ~/.bashrc
      

      需要注意的是, Linux-x86_64-g++-8.4-bmi2-1 这一目录名称是根据编译环境而自动命名的。若您的编译环境与本文环境不同,您需要自行查看具体的目录,并调整之前设置的环境变量路径。

通过配置文件打开数据库

ToplingDB 是一个嵌入式数据库,数据库的库文件直接链接在应用程序中,应用程序通过调用 API 来进行数据库的读写操作。

在本文的所有示例中,我们将数据库放置在路径 /home/topling/db/ 下,也就是用户主目录下的 db 文件夹中。所有编写的代码和配置文件也放在用户主目录 /home/topling/ 下。

  1. 创建配置文件和数据库目录

    执行下列命令建立存放数据库的文件夹。

    1
    2
    3
    
    cd ~
    mkdir -p db
    mkdir -p db/db_mcf
    

    在同一目录下创建配置文件 toplingconf.json ,然后找到我们的示例配置文件,将它里面的配置信息复制进来。

    接下来,修改配置信息中的数据库路径信息 path ,它位于最末尾的 db_mcf 字段中。将它修改为你自己的用户主目录下的db文件夹下的 db_mcf 

    1
    
    "path": "/home/topling/db/db_mcf"
    

    更多关于配置文件的信息,请参阅配置系统介绍

  2. 创建操作数据库的 .cc/.cpp/.cxx 文件

    在用户主空间下,创建包含 main 函数的文件 sample.cpp ,加载我们会用到的头文件 topling/side_plugin_repo.h ,以及标准输入输出流的头文件 iostream 

    1
    2
    
    #include "topling/side_plugin_factory.h"
    #include <iostream>
    

    在主函数中,创建一个 rocksdb::SidePluginRepo 类的实例 repo 。调用它的成员函数 ImportAutoFile ,从我们刚才写好的配置文件中加载配置信息。

    1
    2
    
    rocksdb::SidePluginRepo repo;    // Repo represents of ConfigRepository
    repo.ImportAutoFile("/home/topling/toplingconf.json");
    

    在示例的配置信息中,打开的数据库是 db_mcf ,这是一个包含多个 ColumnFamily 的 DB ,对应类型 rocksdb::DB_MultiCF 。创建一个该类型的指针 dbm 来接收打开的数据库,并将返回的 rocksdb::Status 中的信息打印出来。如果返回的是 OK ,则表示打开成功。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    #include "topling/side_plugin_factory.h"
    #include <iostream>
    
    int main()
    {
        rocksdb::SidePluginRepo repo;
        repo.ImportAutoFile("/home/topling/toplingconf.json");
    
        rocksdb::DB_MultiCF *dbm;
        auto status = repo.OpenDB(&dbm);
        std::cout << status.ToString() << std::endl;
    
        return 0;
    }
    
  3. 编译

    使用以下指令进行编译,输出可执行文件 sample.out 

    1
    
    g++ sample.cpp -I ~/toplingdb/sideplugin/rockside/src -I ~/toplingdb -I ~/toplingdb/sideplugin/topling-zip/src -I ~/toplingdb/sideplugin/topling-zip/boost-include -l:librocksdb.so -DSIDE_PLUGIN_WITH_YAML=1  -DROCKSDB_NO_DYNAMIC_EXTENSION=1 -o sample.out
    

    使用命令 ./sample.out 执行生成的二进制文件。不出意外,我们将看到终端打印出 OK ,这表示我们正确地打开了数据库。

  4. 对数据库的简单读写操作

    在打开数据库后, dbm 中有两个重要的成员变量:指向数据库实例的指针 db 和储存所有 ColumnFamilyHandle 的 vector 容器 cf_handles 

    1
    2
    
    auto db = dbm -> db;
    auto handles = dbm -> cf_handles;
    

    通过它们就可以像操作 RocksDB 一般,对 ToplingDB 进行读写了。如果我们在此基础上增加对输入命令的解析,就成了一个简单的服务式的 KV数据库程序 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    // write
    db -> Put(rocksdb::WriteOptions(), handles[0], rocksdb::Slice("test"), rocksdb::Slice("default_cf");
    db -> Put(rocksdb::WriteOptions(), handles[1], rocksdb::Slice("test"), rocksdb::Slice("custom_cf");
    
    //read
    std::string value1 , value2;
    db -> Get(rocksdb::ReadOptions(), handles[0], rocksdb::Slice("test"), &value1);
    db -> Get(rocksdb::ReadOptions(), handles[1], rocksdb::Slice("test"), &value2);
    std::cout << value1 << std::endl;
    std::cout << value2 << std::endl;
    
    //delete
    status = db -> Delete(rocksdb::WriteOptions(), handles[0], rocksdb::Slice("test"));
    std::cout << status.ToString() << std::endl;
    status = db -> Delete(rocksdb::WriteOptions(), handles[0], rocksdb::Slice("not exist"));
    std::cout << status.ToString() << std::endl;
    

更换 SST Table

ToplingDB 支持旁路插件化,只通过更改配置文件就可以更换 SST 文件的 TableFactory ,无需修改代码。

  • 使用 RocksDB 内置的 SST

    修改配置文件中 TableFactory 的部分,增加不同 Table 类型的配置。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    "TableFactory": {
        "block_based": {
            "class": "BlockBasedTable",
            "params": {
        
            }
        },
        "cuckoo": {
            "class": "CuckooTable",
            "params": {
                
            }
        },
        "plain": {
            "class": "PlainTable",
            "params": {
                
            }
        }
    },
    

    然后在 database 的部分中,使用我们新设置的 table :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    "SliceTransform": {
        "default": {
            "class" : "FixedPrefixTransform",
            "params" :{
                "prefix_len" : 10
            }
        }
    },
    "database": {
        ...
        
        "column_families": {
            "default": "$default",
            "custom_cf" : {
                "max_write_buffer_number": 4,
                "target_file_size_base": "16M",
                "target_file_size_multiplier": 2,
                "table_factory": "block_based",
                "ttl": 0
            },
            "cuckoo_cf" : {
                "table_factory": "cuckoo"
            },
            "plain_cf" : {
                "table_factory": "plain",
                "prefix_extractor" : "$default"
            }
        },
    }
    

    直接运行我们之前的程序,现在打开的数据库中, cuckoo_cf  plain_cf 这两个 ColumnFamily 就已经使用了新的 Table 而不是默认的 BlockBasedTable 。

    如果您在这一步遇到了问题,也可以参考 2-1-toplingconf.json 

  • 使用第三方 SST 文件

    只需要通过 ROCKSDB_FACTORY_REG 宏注册第三方的 Factory ,就可以像使用 RocksDB 内置类型一样使用第三方 SST 文件。

    为了进行一个简单的示范,我们稍微包装一下 BlockBasedTable ,拿它当作一个第三方 SST 文件。

    1. 创建 mysst.h

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      
      // mysst.h
      
      #define ROCKSDB_PLATFORM_POSIX
      #include "table/block_based/block_based_table_factory.h"
      namespace rocksdb
      {
      struct MyBlockBasedTableOptions : public BlockBasedTableOptions {};
          
      class MyBlockBasedTableFactory : public BlockBasedTableFactory
      {
      public:
      explicit MyBlockBasedTableFactory(
            const MyBlockBasedTableOptions& table_options = MyBlockBasedTableOptions());
            const char* Name() const;
      ~MyBlockBasedTableFactory() {};
      };
      
      }
      
    2. 创建 mysst.cpp

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      
      // mysst.cpp
      
      #include "mysst.h"
      #include <iostream>
      
      namespace rocksdb
      {
      
      MyBlockBasedTableFactory::MyBlockBasedTableFactory(const MyBlockBasedTableOptions& _table_options)
           : BlockBasedTableFactory(_table_options) 
           {
               std::cout << "Using MyBlockBasedTableFactory" << std::endl;
           }
      
      const char* MyBlockBasedTableFactory::Name() const
      {
          return "MyBlockBasedTableFactory";
      };
      
      }
      

      可以看到 MyBlockBasedTable 只是继承了 BlockBasedTable 而已,没有其它的改动。只不过当我们使用 MyBlockBasedTable 时,执行它的构造函数会打印出 “Using MyBlockBasedTableFactory” 。

    3. 注册 MyBlockBasedTable

       mysst.cpp 文件中,增加以下部分:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      
      #include "topling/side_plugin_factory.h"
      namespace rocksdb
      {
      
      std::shared_ptr<TableFactory> ThirdSSTExample(const json& js , const SidePluginRepo& repo)
      {
          return std::make_shared<MyBlockBasedTableFactory>(MyBlockBasedTableOptions());
      }
      ROCKSDB_FACTORY_REG("MyBlockBased", ThirdSSTExample);
      }
      

      修改完成后的代码可以参考 2-2-3-mysst.cpp 

      这里为了方便起见,我们总是使用默认的配置项来构造 MyBlockBasedTable 。在实际使用中,您应该通过 js 中保存的 json 信息来构造您使用的 TableFactory ,它类似这样:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      
      std::shared_ptr<TableFactory> ThirdSSTExample(const json& js , const SidePluginRepo& repo)
      {
      	ThirdTableOptions table_options;
      	
          // some code for modifying table_options by json
          ...
          ...
          
          return std::make_shared<ThirdTableFactory>(table_options);
      }
      ROCKSDB_FACTORY_REG("MyBlockBased", ThirdSSTExample);
      
    4. 编译生成 libmysst.so

      执行以下指令进行编译,生成自定义插件 MyBlockBasedTable 的动态库 libmysst.so :

      1
      
      g++ mysst.cpp -I ~/toplingdb -I ~/toplingdb/sideplugin/rockside/src -I ~/toplingdb/sideplugin/topling-zip/src -I ~/toplingdb/sideplugin/topling-zip/boost-include -l:librocksdb.so -fPIC -shared -o libmysst.so
      
    5. 动态加载 libmysst.so :

      设置环境变量 LD_PRELOAD 后,直接运行我们之前的可执行程序 sample.out :

      1
      
      LD_PRELOAD=./libmysst.so ./sample.out
      

      此时 MyBlockBasedTable 已经注册进 ToplingDB ,现在就可以像之前使用 RocksDB 内置的 PlainTable 、 CuckooTable 一般,直接在配置项中启用 MyBlockBasedTable 了。

      在配置文件中进行如下修改,将内置类型 BlockBasedTable 改为第 3 步中,我们用 ROCKSDB_FACTORY_REG 宏注册的名称 “MyBlockBased” 。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      
      "TableFactory": {
          "block_based": {
              "class": "MyBlockBased",
              "params": {
                  
              }
          },
          
          ...
      }
      

      再次运行 sample.out (不要忘记设置 LD_PRELOAD !),就能看到 MyBlockBasedTable 在构造函数中打印的提示信息了。

使用 AnyPlugin 进行 HTML 展示

为了方便,本示例在 sample.cpp 的基础上直接进行修改,没有单独将 HTML 展示插件编译为动态库。

  1. 注册 AnyPlugin 插件

     rocksdb 命名空间内,定义 AnyPlugin 的派生类 HtmlShowExample ,并修改它的 ToString 函数和 Name 函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    namespace rocksdb {
    class HtmlShowExample : public AnyPlugin 
    {
    public:
        void Update(const json&, const SidePluginRepo&) {}
        std::string ToString(const json& dump_options, const SidePluginRepo&) const 
        {
            return "This is an example of HTML show.";
        }
        const char* Name() const
        {
            return "HtmlShowExample";
        }
    };
    }
    

    ToString 函数的返回值是 std::string 类型,其返回的 string 字符串,会被无差别地打印在浏览器中。若返回值是一个序列化的 json 对象, AnyPlugin 还能够以表格的形式展示数据。

    定义派生类 HtmlShowExample 之后,仍然在 rocksdb 命名空间中,使用下列的宏将其注册。

    1
    2
    
    ROCKSDB_REG_DEFAULT_CONS(HtmlShowExample, AnyPlugin);
    ROCKSDB_REG_AnyPluginManip("HtmlShowExample");
    
  2. 开启 http 服务

    在加载配置文件后,调用 repo 的成员函数 StartHttpServer 开启 http 服务。与打开 DB 相似,我们也可以打印出返回的 rocksdb::Status 的相关信息作为参考。

    1
    2
    
    auto http_status = repo.StartHttpServer();
    std::cout << http_status.ToString() << std::endl;
    

    修改后的源程序为 3-2-sample.cpp 

  3. 修改配置文件

    在配置文件的最外层中,增加我们的展示插件信息。

    1
    2
    3
    4
    5
    6
    7
    
    {
        "AnyPlugin": {
            "html-show-example": "HtmlShowExample"
        },
        
        ...
    }
    
  4. 编译并运行项目

    使用我们之前的编译指令编译修改后的 sample.cpp ,并执行程序。

    我们在示例配置文件中设置的监听端口为 8081 ,访问 127.0.0.1:8081/AnyPlugin/html-show-example ,即可看到展示信息。

    如果您不是在本地上执行程序,将 127.0.0.1 更改为您机器的访问ip。如果执行程序打印的信息均为OK,但无法打开页面,请检查防火墙设置。

  5. 其他信息展示

    ToplingDB 内部集成了一个 WebService 用于对外展示内部信息,例如目前配置的参数选项,LSM树的状态,或者是分布式 compact 的执行情况等等。另外,在 Statistic 下展示的监控指标,还可以导入到 Prometheus + Grafana 中进行监控。

    若您还使用了第三方插件,在实现并注册对应的 PluginManipFunc 模板类后,即可在对应的 web 页面下看到 ToString 成员函数返回的序列化信息。

浏览 10
点赞
评论
收藏
分享

手机扫一扫分享

编辑 分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

编辑 分享
举报