VS2015編譯RocksDB
RocksDB 是一個來自 facebook 的可嵌入式的支持持久化的 key-value 存儲系統,也可作為 C/S 模式下的存儲數據庫,但主要目的還是嵌入式。RocksDB 基於 LevelDB 構建。
1、下載rocksdb源碼
git clone https://github.com/facebook/rocksdb.git
2、使用CMAKE生成VS工程
打開cmd窗口(最好使用VS2015開發人員命令提示
),進入源碼目錄,執行下面命令
mkdir msvc14 # 創建構建目錄
cd msvc14 # 進入構建目錄
# 生成VS項目(D:\Libs\rocksdb是編譯后安裝路徑)
cmake -DCMAKE_INSTALL_PREFIX=D:\Libs\rocksdb -G "Visual Studio 14 Win64" ..
實際上rocksdb源碼目錄中的CMakeList.txt
文件中並沒有寫INSTALL
段的內容,所以這里指定安裝路徑實際是無效的。
3、編譯RocksDB
打開VS2015 x64 本機工具命令提示
(或VS2015開發人員命令提示)工具,執行下面命令進行編譯64位release版本的RocksDB庫
msbuild ALL_BUILD.vcproject /p:configuration=release /maxcpucount:8
因為test項目也都編譯了,文件較多,所以這里使用了/maxcpucount:8
指定使用的CPU核心數。
如果只編譯rocksdb(靜態)庫,可以使用msbuild rocksdblib.vcproject /p:configuration=release
命令
編譯動態庫使用msbuild rocksdb.vcproject /p:configuration=release
命令
編譯完成后,通過的做法是使用下面命令進行安裝
msbuild INSTALL.vcproject /p:configuration=release
但是這里沒有生成INSTALL.vcproject,所以這里的安裝需要手動進行。
直接拷貝msvc14/Release
目錄下的rocksdblib.lib
文件和源碼目錄的include
目錄即可。
4、編譯后續
因為編譯出來的是動態庫,但是實際上RocksDB
的代碼中比沒有使用__declspec(dllexport)
導出函數和類接口,所以在使用的時候會遇到類似如下的問題
rocksdb_test.obj : error LNK2019: 無法解析的外部符號 "public: __cdecl rocksdb::ColumnFamilyOptions::ColumnFamilyOptions(void)" (??0ColumnFamilyOptions@rocksdb@@QEAA@XZ),該符號在函數 "public: __cdecl rocksdb::Options::Options(void)" (??0Options@rocksdb@@QEAA@XZ) 中被引用
rocksdb_test.obj : error LNK2019: 無法解析的外部符號 "public: __cdecl rocksdb::DBOptions::DBOptions(void)" (??0DBOptions@rocksdb@@QEAA@XZ),該符號在函數 "public: __cdecl rocksdb::Options::Options(void)" (??0Options@rocksdb@@QEAA@XZ) 中被引用
rocksdb_test.obj : error LNK2019: 無法解析的外部符號 "public: __cdecl rocksdb::ReadOptions::ReadOptions(void)" (??0ReadOptions@rocksdb@@QEAA@XZ),該符號在函數 main 中被引用
rocksdb_test.obj : error LNK2019: 無法解析的外部符號 "public: static class rocksdb::Status __cdecl rocksdb::DB::Open(struct rocksdb::Options const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class rocksdb::DB * *)" (?Open@DB@rocksdb@@SA?AVStatus@2@AEBUOptions@2@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@PEAPEAV12@@Z),該符號在函數 main 中被引用
rocksdb_test.exe : fatal error LNK1120: 4 個無法解析的外部命令
這里可以打開生成的VS工程,將配置類型改為靜態庫,然后再生成靜態庫即可。
生成后即可使用了。但是還會遇到下面的問題
rocksdb.lib(env_win.obj) : error LNK2019: 無法解析的外部符號 __imp_RpcStringFreeA,該符號在函數 "public: virtual class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl rocksdb::Env::GenerateUniqueId(void)" (?GenerateUniqueId@Env@rocksdb@@UEAA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ) 中被引用
rocksdb.lib(env_win.obj) : error LNK2019: 無法解析的外部符號 __imp_UuidCreateSequential,該符號在函數 "public: virtual class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl rocksdb::Env::GenerateUniqueId(void)" (?GenerateUniqueId@Env@rocksdb@@UEAA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ) 中被引用
rocksdb.lib(env_win.obj) : error LNK2019: 無法解析的外部符號 __imp_UuidToStringA,該符號在函數 "public: virtual class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl rocksdb::Env::GenerateUniqueId(void)" (?GenerateUniqueId@Env@rocksdb@@UEAA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ) 中被引用
rocksdb_test.exe : fatal error LNK1120: 3 個無法解析的外部命令
這些函數實際上是在Rpcrt4.lib
這個庫里面,所以需要鏈接上才行。可以在使用的時候在代碼中添加#pragma comment(lib,"Rpcrt4.lib")
來引入。
相關測試代碼如下
#include <cassert>
#include <string>
#include <iostream>
#include <chrono>
#include "rocksdb/db.h"
#pragma comment(lib,"Rpcrt4.lib")
#define TEST_FREQUENCY (10000)
char* randomstr()
{
static char buf[1024];
int len = rand() % 768 + 255;
for (int i = 0; i < len; ++i) {
buf[i] = 'A' + rand() % 26;
}
buf[len] = '\0';
return buf;
}
int main()
{
rocksdb::DB* db;
rocksdb::Options options;
options.create_if_missing = true;
// 打開數據庫
rocksdb::Status status = rocksdb::DB::Open(options, "./testdb", &db);
assert(status.ok());
srand(2017);
std::string k[TEST_FREQUENCY];
for (int i = 0; i < TEST_FREQUENCY; ++i) {
k[i] = (randomstr());
}
std::string v("壹貳叄肆伍陸柒捌玖拾");
v.append(v).append(v).append(v).append(v).append(v);
// 測試添加
{
auto start = std::chrono::system_clock::now();
for (int i = 0; i < TEST_FREQUENCY; ++i) {
status = db->Put(rocksdb::WriteOptions(), k[i], v);
assert(status.ok());
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << TEST_FREQUENCY <<"次添加耗時: "
<< double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den
<< "秒" << std::endl;
}
// 測試獲取
{
auto start = std::chrono::system_clock::now();
std::string v2[TEST_FREQUENCY];
for (int i = 0; i < TEST_FREQUENCY; ++i) {
status = db->Get(rocksdb::ReadOptions(), k[i], &v2[i]);
assert(status.ok());
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << TEST_FREQUENCY <<"次獲取耗時: "
<< double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den
<< "秒" << std::endl;
// 驗證獲取結果是否正確
std::string ss;
for (int i = 0; i < TEST_FREQUENCY; ++i) {
if (v2[i] != v) {
std::cout << "第 " << i << " 個結果不正確" << std::endl;
std::cout << v2[i] << std::endl;
}
}
}
// 測試修改
{
auto start = std::chrono::system_clock::now();
v.append(v);
for (int i = 0; i < TEST_FREQUENCY; ++i) {
status = db->Put(rocksdb::WriteOptions(), k[i], v);
assert(status.ok());
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << TEST_FREQUENCY <<"次修改耗時: "
<< double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den
<< "秒" << std::endl;
}
// 測試刪除
{
auto start = std::chrono::system_clock::now();
for (int i = 0; i < TEST_FREQUENCY; ++i) {
status = db->Delete(rocksdb::WriteOptions(), k[i]);
assert(status.ok());
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << TEST_FREQUENCY <<"次刪除耗時: "
<< double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den
<< "秒" << std::endl;
}
delete db;
return 0;
}
編譯命令如下:
# Debug版本
cl rocksdb_test.cpp /I ..\..\libs\vs140-x64\rocksdb\include ..\..\libs\vs140-x64\rocksdb\Debug\rocksdb.lib /MDd /EHsc \
/GF /TP /Zp8 /Od /Gy /W3 /D "OS_WIN" /D "WIN32" /D "_WINDOWS" /D "NDEBUG" /D "ROCKSDB_LITE" /D "_MBCS" /D "WIN64" /D "NOMINMAX"
# Release版本
cl rocksdb_test.cpp /I ..\..\libs\vs140-x64\rocksdb\include ..\..\libs\vs140-x64\rocksdb\Release\rocksdb.lib /MD /EHsc \
/GF /TP /Zp8 /O2 /Gy /W3 /D "OS_WIN" /D "WIN32" /D "_WINDOWS" /D "NDEBUG" /D "ROCKSDB_LITE" /D "_MBCS" /D "WIN64" /D "NOMINMAX"
注意上面使用了/Zp8
和/TP
/GF
等選項。如果不設置/Zp8(8字節對齊)
,在運行時候會拋出異常。因為VS編譯64位程序的時候,默認就是8字節對齊,而我沒有測試32位版本的,所以這里可能不是關鍵。
后話
Linux下的編譯都比較簡單,包括LevelDB
等,都只需要直接make
即可。Linux下編譯的時候可能有某些依賴需要安裝一下,包括gflags
、jemalloc
、libsnappy
、libbz2
、liblz4
等。