Windows下 VS2015編譯RocksDB


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下編譯的時候可能有某些依賴需要安裝一下,包括gflagsjemalloclibsnappylibbz2liblz4等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM