ToplingDB 是由北京拓扑岭科技有限公司出品的一款开源数据库引擎。Fock 自 RocksDB 并进行内核改造,以独有的“可检索内存压缩”和“分布式 compact”等技术手段,大大提升数据库引擎的性能和可用性。
服务器环境
操作系统: CentOS Linux release 8.4.2105
g++版本: g++ (GCC) 8.4.1 20200928 (Red Hat 8.4.1-1)
安装相关依赖
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 |
安装 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 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" |
更多关于配置文件的信息,请参阅配置系统介绍。
创建操作数据库的 .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; } |
编译
使用以下指令进行编译,输出可执行文件 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 ,这表示我们正确地打开了数据库。
对数据库的简单读写操作
在打开数据库后, 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; |
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 文件。
创建 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() {}; }; } |
创建 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” 。
注册 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); |
编译生成 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 |
动态加载 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 在构造函数中打印的提示信息了。
为了方便,本示例在 sample.cpp 的基础上直接进行修改,没有单独将 HTML 展示插件编译为动态库。
注册 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"); |
开启 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 。
修改配置文件
在配置文件的最外层中,增加我们的展示插件信息。
1 2 3 4 5 6 7 |
{ "AnyPlugin": { "html-show-example": "HtmlShowExample" }, ... } |
编译并运行项目
使用我们之前的编译指令编译修改后的 sample.cpp
,并执行程序。
我们在示例配置文件中设置的监听端口为 8081 ,访问 127.0.0.1:8081/AnyPlugin/html-show-example
,即可看到展示信息。
如果您不是在本地上执行程序,将 127.0.0.1
更改为您机器的访问ip。如果执行程序打印的信息均为OK,但无法打开页面,请检查防火墙设置。
其他信息展示
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