ToplingDB

高性能 K-V 数据库引擎
授权协议 Apache
开发语言 C/C++
所属分类 数据库相关、 数据库服务器
软件类型 开源软件
地区 国产
投 递 者 阎功
操作系统 Linux
开源组织
适用人群 未知
 软件概览

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 成员函数返回的序列化信息。

  • 本文原始链接:101 · topling/rockside Wiki 从 RocksDB 迁移到 ToplingDB,只需要修改 Open/Close 相关的代码,其它代码不用任何改动。 示例代码(省略错误处理,完整版在这里) #include <topling/side_plugin_repo.h> #include <rocksdb/db.h> int main(int argc, char*

  • ToplingDB fork 自 RocksDB,自然而然地兼容了 RocksDB 的生态,但是,兼容 RocksDB 有优点,就自然有缺点,例如 RocksDB 的 L0 Flush 在我们看来就完全是多余的,但是,要去掉 L0 Flush,并且在 RocksDB 的巨大的 Code Base 上动刀,难度杠杠的,不过,至少,我们的思路是清晰的: 省略 L0 Flush L0 中的数据很新,即便

  • 背景 在 RocksDB 中,有大量 XXXXFactory 这样的类,例如 TableFactory,它用来实现 SST 文件的插件化,用户要换一种 SST,只需要配置一个相应的 TableFactory 即可,这其实已经很灵活了。 问题 这种简单的 Factory 机制存在两个问题: 要更换 Factory,用户必须修改代码,这虽然有点繁琐,但不算致命 致命的是:如果要更换的是第三方的 Fac

  • 1. 概述 简而言之,编程语言中的反射(Reflection)指的是从运行时中获取语言本身的类型等信息。C++ 缺乏这样的机制,对于最简单的 enum 类型,我们或许可以实现带有反射功能的 enum。 我们实现了几个宏,通过宏定义的 enum,就自动地拥有反射功能。 2. 用法 2.2 宏定义 // 可在任意 namespace 中调用,不可在 struct/class 内调用 #define R

  • 按照 ToplingDB 的设计哲学,至少在软件层面,反向扫描没有理由比正向扫描更慢。 然而,这个被我们认为是自然而然的事情,却被很多主流的数据库认为是极难实现,甚至不可能实现的。于是,他们发明了一种叫做 降序索引 的东西: 数据库 降序索引(或其变形) MySQL 8.0 Descending Index MyRocks (MySQL + RocksDB) Reverse Column Fami

 相关资料
  • 问题内容: 我需要一些想法来实现Java的(真正)高性能内存数据库/存储机制。在存储20,000+个Java对象的范围内,每5秒钟左右更新一次。 我愿意接受的一些选择: 纯JDBC /数据库组合 JDO JPA / ORM /数据库组合 对象数据库 其他存储机制 我最好的选择是什么?你有什么经验? 编辑:我还需要能够查询这些对象 问题答案: 您可以尝试使用Prevayler之类的工具(基本上是一个

  • 问题内容: 我在公司中多次设计数据库。为了提高数据库的性能,我只寻找标准化和索引。 如果要求您提高数据库的性能,该数据库包含大约250个表以及一些具有数百万个记录的表,那么您将寻找什么不同的东西? 提前致谢。 问题答案: 优化逻辑设计 逻辑级别是关于查询和表本身的结构。首先尝试最大程度地发挥这一作用。目标是在逻辑级别上访问尽可能少的数据。 拥有最高效的SQL查询 设计支持应用程序需求的逻辑架构(例

  • 我们正在快速开发一个应用程序,其中我们需要一次获取超过50K行(在应用程序加载时执行),然后数据将用于应用程序的其他部分进行进一步计算。我们正在使用Firebase实时数据库,我们面临一些严重的性能问题。 它目前需要大约40秒才能加载50K行(目前使用的是免费数据库版本,不确定这是否是原因),但我们也观察到,当多个用户使用该应用程序时,加载50K行开始需要大约1分20秒,Peak达到100%。 您

  • Kdb+ 是来自 Kx Systems Inc 的高性能列式数据库。 kdb+ 旨在捕获,分析,比较和存储数据 - 所有这些都是高速和大量数据。

  • 主要内容:一般业务系统运行流程图,一台 4 核 8G 的机器能扛多少并发量呢?,高并发来袭时数据库会先被打死吗?,8 核 16G 的数据库每秒大概可以抗多少并发压力?,数据库架构可以从哪些方面优化?,总结今天给大家分享一个知识点,是关于 MySQL 数据库架构演进的,因为很多兄弟天天基于 MySQL 做系统开发,但是写的系统都是那种低并发压力、小数据量的,所以哪怕上线了也就是这么正常跑着而已。 但是你知道你连接的这个 MySQL 数据库他到底能抗多大并发压力吗?如果 MySQL 数据库扛不住压力

  • 我们创建了一个程序,以便在其他程序中更容易地使用数据库。因此,我显示的代码将在多个其他程序中使用。 其中一个程序从我们的一个客户那里获得大约10000条记录,并且必须检查这些记录是否已经存在于我们的数据库中。如果没有,我们将它们插入数据库(它们也可以更改,然后必须更新)。 为了方便起见,我们从整个表中加载所有条目(目前为120,000个),为我们得到的每个条目创建一个类,并将它们全部放入Hashm