ToplingDB高性能 K-V 數(shù)據(jù)庫(kù)引擎
ToplingDB 是由北京拓?fù)鋷X科技有限公司出品的一款開(kāi)源數(shù)據(jù)庫(kù)引擎。Fock 自 RocksDB 并進(jìn)行內(nèi)核改造,以獨(dú)有的“可檢索內(nèi)存壓縮”和“分布式 compact”等技術(shù)手段,大大提升數(shù)據(jù)庫(kù)引擎的性能和可用性。
部署安裝 ToplingDB
-
服務(wù)器環(huán)境
操作系統(tǒng): CentOS Linux release 8.4.2105
g++版本: g++ (GCC) 8.4.1 20200928 (Red Hat 8.4.1-1)
-
安裝相關(guān)依賴(lài)
ToplingDB 基于 RocksDB 構(gòu)建,我們需要用到壓縮庫(kù) snappy 和命令行參數(shù)解析工具 gflags 。除此之外,在編譯的過(guò)程中,還需要用到 libaio 的開(kāi)發(fā)包。
-
安裝 snappy :
1sudo yum install snappy snappy-devel -
安裝 gflags :
-
對(duì)于 CentOS 8:
1sudo dnf --enablerepo=powertools install gflags-devel -
對(duì)于 CentOS 7(需要 EPEL ):
1sudo yum install gflags-devel
-
-
安裝 libaio-devel :
1sudo yum install libaio-devel
-
-
安裝 ToplingDB
-
獲取項(xiàng)目源代碼:
1 2
cd ~ git clone https://github.com/topling/toplingdb.git
-
更新依賴(lài)的子項(xiàng)目:
1 2
cd toplingdb git submodule update --init --recursive
-
編譯安裝動(dòng)態(tài)庫(kù):
1 2
make shared_lib sudo make install
-
設(shè)置環(huán)境變量:
除了 librocksdb.so 之外,我們還會(huì)用到 topling-zip 編譯生成的 libterark-zbs-r.so 等動(dòng)態(tài)庫(kù)。在剛才的 make 過(guò)程中, topling-zip 已被克隆到
toplingdb/sideplugin目錄下,它編譯得到的動(dòng)態(tài)庫(kù)位于topling-zip/build/Linux-x86_64-g++-8.4-bmi2-1/lib_shared。打開(kāi)文件
~/.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
保存后,執(zhí)行以下命令,更新我們的設(shè)置:
1source ~/.bashrc需要注意的是,
Linux-x86_64-g++-8.4-bmi2-1這一目錄名稱(chēng)是根據(jù)編譯環(huán)境而自動(dòng)命名的。若您的編譯環(huán)境與本文環(huán)境不同,您需要自行查看具體的目錄,并調(diào)整之前設(shè)置的環(huán)境變量路徑。
-
通過(guò)配置文件打開(kāi)數(shù)據(jù)庫(kù)
ToplingDB 是一個(gè)嵌入式數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)的庫(kù)文件直接鏈接在應(yīng)用程序中,應(yīng)用程序通過(guò)調(diào)用 API 來(lái)進(jìn)行數(shù)據(jù)庫(kù)的讀寫(xiě)操作。
在本文的所有示例中,我們將數(shù)據(jù)庫(kù)放置在路徑 /home/topling/db/ 下,也就是用戶(hù)主目錄下的 db 文件夾中。所有編寫(xiě)的代碼和配置文件也放在用戶(hù)主目錄 /home/topling/ 下。
-
創(chuàng)建配置文件和數(shù)據(jù)庫(kù)目錄
執(zhí)行下列命令建立存放數(shù)據(jù)庫(kù)的文件夾。
1 2 3
cd ~ mkdir -p db mkdir -p db/db_mcf
在同一目錄下創(chuàng)建配置文件
toplingconf.json,然后找到我們的示例配置文件,將它里面的配置信息復(fù)制進(jìn)來(lái)。接下來(lái),修改配置信息中的數(shù)據(jù)庫(kù)路徑信息
path,它位于最末尾的db_mcf字段中。將它修改為你自己的用戶(hù)主目錄下的db文件夾下的db_mcf。1"path": "/home/topling/db/db_mcf"更多關(guān)于配置文件的信息,請(qǐng)參閱配置系統(tǒng)介紹。
-
創(chuàng)建操作數(shù)據(jù)庫(kù)的 .cc/.cpp/.cxx 文件
在用戶(hù)主空間下,創(chuàng)建包含 main 函數(shù)的文件
sample.cpp,加載我們會(huì)用到的頭文件topling/side_plugin_repo.h,以及標(biāo)準(zhǔn)輸入輸出流的頭文件iostream。1 2
#include "topling/side_plugin_factory.h" #include <iostream>
在主函數(shù)中,創(chuàng)建一個(gè)
rocksdb::SidePluginRepo類(lèi)的實(shí)例repo。調(diào)用它的成員函數(shù)ImportAutoFile,從我們剛才寫(xiě)好的配置文件中加載配置信息。1 2
rocksdb::SidePluginRepo repo; // Repo represents of ConfigRepository repo.ImportAutoFile("/home/topling/toplingconf.json");
在示例的配置信息中,打開(kāi)的數(shù)據(jù)庫(kù)是
db_mcf,這是一個(gè)包含多個(gè) ColumnFamily 的 DB ,對(duì)應(yīng)類(lèi)型rocksdb::DB_MultiCF。創(chuàng)建一個(gè)該類(lèi)型的指針dbm來(lái)接收打開(kāi)的數(shù)據(jù)庫(kù),并將返回的rocksdb::Status中的信息打印出來(lái)。如果返回的是 OK ,則表示打開(kāi)成功。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; }
-
編譯
使用以下指令進(jìn)行編譯,輸出可執(zhí)行文件
sample.out。1g++ 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執(zhí)行生成的二進(jìn)制文件。不出意外,我們將看到終端打印出 OK ,這表示我們正確地打開(kāi)了數(shù)據(jù)庫(kù)。 -
對(duì)數(shù)據(jù)庫(kù)的簡(jiǎn)單讀寫(xiě)操作
在打開(kāi)數(shù)據(jù)庫(kù)后,
dbm中有兩個(gè)重要的成員變量:指向數(shù)據(jù)庫(kù)實(shí)例的指針db和儲(chǔ)存所有 ColumnFamilyHandle 的 vector 容器cf_handles。1 2
auto db = dbm -> db; auto handles = dbm -> cf_handles;
通過(guò)它們就可以像操作 RocksDB 一般,對(duì) ToplingDB 進(jìn)行讀寫(xiě)了。如果我們?cè)诖嘶A(chǔ)上增加對(duì)輸入命令的解析,就成了一個(gè)簡(jiǎn)單的服務(wù)式的 KV數(shù)據(jù)庫(kù)程序 。
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 支持旁路插件化,只通過(guò)更改配置文件就可以更換 SST 文件的 TableFactory ,無(wú)需修改代碼。
-
使用 RocksDB 內(nèi)置的 SST
修改配置文件中 TableFactory 的部分,增加不同 Table 類(lèi)型的配置。
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 的部分中,使用我們新設(shè)置的 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" } }, }
直接運(yùn)行我們之前的程序,現(xiàn)在打開(kāi)的數(shù)據(jù)庫(kù)中,
cuckoo_cf和plain_cf這兩個(gè) ColumnFamily 就已經(jīng)使用了新的 Table 而不是默認(rèn)的 BlockBasedTable 。如果您在這一步遇到了問(wèn)題,也可以參考 2-1-toplingconf.json 。
-
使用第三方 SST 文件
只需要通過(guò)
ROCKSDB_FACTORY_REG宏注冊(cè)第三方的 Factory ,就可以像使用 RocksDB 內(nèi)置類(lèi)型一樣使用第三方 SST 文件。為了進(jìn)行一個(gè)簡(jiǎn)單的示范,我們稍微包裝一下 BlockBasedTable ,拿它當(dāng)作一個(gè)第三方 SST 文件。
-
創(chuàng)建 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() {}; }; }
-
創(chuàng)建 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 而已,沒(méi)有其它的改動(dòng)。只不過(guò)當(dāng)我們使用 MyBlockBasedTable 時(shí),執(zhí)行它的構(gòu)造函數(shù)會(huì)打印出 “Using MyBlockBasedTableFactory” 。
-
注冊(cè) 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 。
這里為了方便起見(jiàn),我們總是使用默認(rèn)的配置項(xiàng)來(lái)構(gòu)造 MyBlockBasedTable 。在實(shí)際使用中,您應(yīng)該通過(guò)
js中保存的 json 信息來(lái)構(gòu)造您使用的 TableFactory ,它類(lèi)似這樣: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
執(zhí)行以下指令進(jìn)行編譯,生成自定義插件 MyBlockBasedTable 的動(dòng)態(tài)庫(kù) libmysst.so :
1g++ 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 -
動(dòng)態(tài)加載 libmysst.so :
設(shè)置環(huán)境變量
LD_PRELOAD后,直接運(yùn)行我們之前的可執(zhí)行程序sample.out:1LD_PRELOAD=./libmysst.so ./sample.out此時(shí) MyBlockBasedTable 已經(jīng)注冊(cè)進(jìn) ToplingDB ,現(xiàn)在就可以像之前使用 RocksDB 內(nèi)置的 PlainTable 、 CuckooTable 一般,直接在配置項(xiàng)中啟用 MyBlockBasedTable 了。
在配置文件中進(jìn)行如下修改,將內(nèi)置類(lèi)型
BlockBasedTable改為第 3 步中,我們用ROCKSDB_FACTORY_REG宏注冊(cè)的名稱(chēng) “MyBlockBased” 。1 2 3 4 5 6 7 8 9 10
"TableFactory": { "block_based": { "class": "MyBlockBased", "params": { } }, ... }
再次運(yùn)行
sample.out(不要忘記設(shè)置LD_PRELOAD?。?,就能看到 MyBlockBasedTable 在構(gòu)造函數(shù)中打印的提示信息了。
-
使用 AnyPlugin 進(jìn)行 HTML 展示
為了方便,本示例在 sample.cpp 的基礎(chǔ)上直接進(jìn)行修改,沒(méi)有單獨(dú)將 HTML 展示插件編譯為動(dòng)態(tài)庫(kù)。
-
注冊(cè) AnyPlugin 插件
在
rocksdb命名空間內(nèi),定義AnyPlugin的派生類(lèi)HtmlShowExample,并修改它的ToString函數(shù)和Name函數(shù)。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函數(shù)的返回值是 std::string 類(lèi)型,其返回的 string 字符串,會(huì)被無(wú)差別地打印在瀏覽器中。若返回值是一個(gè)序列化的 json 對(duì)象, AnyPlugin 還能夠以表格的形式展示數(shù)據(jù)。定義派生類(lèi)
HtmlShowExample之后,仍然在rocksdb命名空間中,使用下列的宏將其注冊(cè)。1 2
ROCKSDB_REG_DEFAULT_CONS(HtmlShowExample, AnyPlugin); ROCKSDB_REG_AnyPluginManip("HtmlShowExample");
-
開(kāi)啟 http 服務(wù)
在加載配置文件后,調(diào)用 repo 的成員函數(shù)
StartHttpServer開(kāi)啟 http 服務(wù)。與打開(kāi) DB 相似,我們也可以打印出返回的rocksdb::Status的相關(guān)信息作為參考。1 2
auto http_status = repo.StartHttpServer(); std::cout << http_status.ToString() << std::endl;
修改后的源程序?yàn)?span> 3-2-sample.cpp 。
-
修改配置文件
在配置文件的最外層中,增加我們的展示插件信息。
1 2 3 4 5 6 7
{ "AnyPlugin": { "html-show-example": "HtmlShowExample" }, ... }
-
編譯并運(yùn)行項(xiàng)目
使用我們之前的編譯指令編譯修改后的
sample.cpp,并執(zhí)行程序。我們?cè)谑纠渲梦募性O(shè)置的監(jiān)聽(tīng)端口為 8081 ,訪(fǎng)問(wèn)
127.0.0.1:8081/AnyPlugin/html-show-example,即可看到展示信息。如果您不是在本地上執(zhí)行程序,將
127.0.0.1更改為您機(jī)器的訪(fǎng)問(wèn)ip。如果執(zhí)行程序打印的信息均為OK,但無(wú)法打開(kāi)頁(yè)面,請(qǐng)檢查防火墻設(shè)置。 -
其他信息展示
ToplingDB 內(nèi)部集成了一個(gè) WebService 用于對(duì)外展示內(nèi)部信息,例如目前配置的參數(shù)選項(xiàng),LSM樹(shù)的狀態(tài),或者是分布式 compact 的執(zhí)行情況等等。另外,在
Statistic下展示的監(jiān)控指標(biāo),還可以導(dǎo)入到 Prometheus + Grafana 中進(jìn)行監(jiān)控。若您還使用了第三方插件,在實(shí)現(xiàn)并注冊(cè)對(duì)應(yīng)的 PluginManipFunc 模板類(lèi)后,即可在對(duì)應(yīng)的 web 頁(yè)面下看到
ToString成員函數(shù)返回的序列化信息。
