FreeBSDで,Google leveldbを使ってみた.
まだ性能検証はしていませんが,記述を見るだけで凄そうな予感がしたGoogle謹製の新NoSQL.
GitHub - google/leveldb: LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.
コードを読む限り,想像以上に凄い感じでした.ドキュメント指向ではありませんが,valueにかなり自由度があるので(アスキーコードの非表示コードが入っていても良い.バイト長さえ分かっていて指定すれば,任意のバイナリデータが格納可能),なんでもできそうです.
まずは,FreeBSDにインストールしてちゃんと動くようにするまで3日かかった...
まとまったソースは今のところ配布されていないので,subversionでリポジトリを落とします.
> svn co http://leveldb.googlecode.com/svn/trunk/ leveldb-read-only
HTTPアクセスにproxy設定が必要な場合は,こんな感じ.
> svn co --config-option servers:global:http-proxy-host=proxy.example.com --config-option servers:global:http-proxy-port=123456 http://leveldb.googlecode.com/svn/trunk/ leveldb-read-only
configureも無くてMakefileしか無いので,まずは普通にmake.
> gmake g++ -c -I. -I./include -DLEVELDB_PLATFORM_POSIX -std=c++0x -g2 db/db_bench.cc -o db/db_bench.o cc1plus: error: unrecognized command line option "-std=c++0x" gmake: *** [db/db_bench.o] エラー 1
デフォルトのg++はc++0xに対応していない...
portsコレクションから,ことごとく試してみました.
FreeBSD8では,portsではgcc-4.4からgcc-4.7まで入れることができます.
最新の4.6でやってみました.
(4.7はまだ不安定)
Makefileを書き換えます.
//CC = g++ CC=g++46
makeします.
g++46 -c -DLEVELDB_PLATFORM_POSIX -I. -I./include -std=c++0x -g2 db/db_bench.cc -o db/db_bench.o In file included from ./port/port.h:14:0, from ./util/coding.h:17, from ./db/dbformat.h:13, from ./db/db_impl.h:9, from db/db_bench.cc:8: ./port/port_posix.h:10:20: fatal error: endian.h: No such file or directory compilation terminated. *** Error code 1 Stop in /home/hoge/leveldb-read-only.
FreeBSDでは微妙にパスが違うんです...port_posix.hを書き換えます.
//#include <endian.h> #include <sys/endian.h>
またmake.
g++46 -c -DLEVELDB_PLATFORM_POSIX -I. -I./include -std=c++0x -g2 db/db_bench.cc -o db/db_bench.o In file included from ./port/port.h:14:0, from ./util/coding.h:17, from ./db/dbformat.h:13, from ./db/db_impl.h:9, from db/db_bench.cc:8: ./port/port_posix.h:15:22: fatal error: cstdatomic: No such file or directory compilation terminated. *** Error code 1 Stop in /home/hoge/leveldb-read-only.
これはよく分からなかったのですが,includeファイルを調べるとファイル名が違うっぽいので,またソースport_posix.hを書き換えます.
//#include <cstdatomic> #include <atomic>
またまたmake.
> gmake g++46 -c -DLEVELDB_PLATFORM_POSIX -I. -I./include -std=c++0x -g2 db/db_bench.cc -o db/db_bench.o In file included from ./port/port.h:14, from ./util/coding.h:17, from ./db/dbformat.h:13, from ./db/db_impl.h:9, from db/db_bench.cc:8: ./port/port_posix.h:21: error: '__BYTE_ORDER' was not declared in this scope ./port/port_posix.h:21: error: '__LITTLE_ENDIAN' was not declared in this scope *** Error code 1 Stop in /home/hoge/leveldb-read-only.
これはー...定数の定義がFreeBSDはちょっと違うのです.ソースを書き換えます.
//static const bool kLittleEndian = (__BYTE_ORDER == __LITTLE_ENDIAN); static const bool kLittleEndian = (_BYTE_ORDER == _LITTLE_ENDIAN);
またまたまたmake.
g++46 -c -DLEVELDB_PLATFORM_POSIX -I. -I./include -std=c++0x -g2 ./util/cache.cc -o ./util/cache.o ./util/cache.cc: In member function 'void leveldb::<unnamed>::LRUCache::Unref(leveldb::<unnamed>::LRUHandle*)': ./util/cache.cc:148: error: 'free' was not declared in this scope ./util/cache.cc: In member function 'virtual leveldb::Cache::Handle* leveldb::<unnamed>::LRUCache::Insert(const leveldb::Slice&, void*, size_t, void (*)(const leveldb::Slice&, void*))': ./util/cache.cc:197: error: 'malloc' was not declared in this scope *** Error code 1 Stop in /home/hoge/leveldb-read-only.
これはバグなんじゃね?って感じです.mallocやfree使うのにヘッダファイルがincludeされてない...ソースを修正します.
#include <stdlib.h>
またまたまたまたmake.
g++46 -c -DLEVELDB_PLATFORM_POSIX -I. -I./include -std=c++0x -g2 ./util/env_posix.cc -o ./util/env_posix.o ./util/env_posix.cc: In member function 'virtual leveldb::Status leveldb::<unnamed>::PosixSequentialFile::Read(size_t, leveldb::Slice*, char*)': ./util/env_posix.cc:43: error: 'fread_unlocked' was not declared in this scope ./util/env_posix.cc: In member function 'virtual leveldb::Status leveldb::<unnamed>::PosixMmapFile::Sync()': ./util/env_posix.cc:230: error: 'fdatasync' was not declared in this scope *** Error code 1 Stop in /home/hoge/leveldb-read-only.
これは厳しい.fdatasyncやfread_unlockedはFreeBSDはまだサポートしていないんです.
仕方が無いので,遅い関数名に書き換えます.
// size_t r = fread_unlocked(scratch, 1, n, file_); size_t r = fread(scratch, 1, n, file_); // if (fdatasync(fd_) < 0) { if (fsync(fd_) < 0) {
またまたまたまたまたmake.
リポジトリの最初の頃は違いましたが,現在はアーカイブライブラリを作るようになっているので,*.oじゃなくて*.aをリンクすれば良いようになっています.
テストプログラムを走らせると,
> ./arena_test /libexec/ld-elf.so.1: /usr/lib/libstdc++.so.6: version GLIBCXX_3.4.10 required by /home/hoge/leveldb-read-only/arena_test not found
おっと,これはgccのバージョンを混在させているためなので,
> env LD_LIBRARY_PATH=/usr/local/lib/gcc46/ ./arena_test ==== Test ArenaTest.Empty ==== Test ArenaTest.Simple ==== PASSED 2 tests
OK!
次,肝心なデータベースのテストを走らせると
> env LD_LIBRARY_PATH=/usr/local/lib/gcc46/ ./db_test ==== Test DBTest.Empty Assertion failed: (internal_key.size() >= 8), function ExtractUserKey, file ./db/dbformat.h, line 87. Abort (core dumped)
あり?
散々悩んだあげく,またコンパイラのバージョンを変えてみると
CC = g++45
イケた.
> env LD_LIBRARY_PATH=/usr/local/lib/gcc45/ ./db_test ==== Test DBTest.Empty ==== Test DBTest.ReadWrite ==== Test DBTest.PutDeleteGet ==== Test DBTest.IterEmpty ==== Test DBTest.IterSingle ==== Test DBTest.IterMulti ==== Test DBTest.IterSmallAndLargeMix ==== Test DBTest.Recover ==== Test DBTest.RecoveryWithEmptyLog ==== Test DBTest.MinorCompactionsHappen ==== Test DBTest.RecoverWithLargeLog ==== Test DBTest.CompactionsGenerateMultipleFiles ==== Test DBTest.SparseMerge Step 9900 of 10000 2705 entries compared: ok=1 2684 entries compared: ok=1 2705 entries compared: ok=1 ==== PASSED 24 tests Ok!
putとgetのマックス最小のプログラムも通りました.
データの書き込み.
#include <cassert> #include <iostream> #include "leveldb/db.h" int main() { leveldb::DB* db=NULL; leveldb::Options options; options.create_if_missing = true; leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db); assert(status.ok()); leveldb::Slice key = "Hello,"; std::string val; status = db->Get(leveldb::ReadOptions(), key, &val); if(!status.ok()) std::cout<<status.ToString()<<std::endl; std::cout<<key.ToString()<<val<<std::endl; delete db; return(0); }
データの読み込み.
#include <cassert> #include <iostream> #include "leveldb/db.h" int main() { leveldb::DB* db=NULL; leveldb::Options options; options.create_if_missing = true; leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db); assert(status.ok()); leveldb::Slice key = "Hello,"; std::string val; status = db->Get(leveldb::ReadOptions(), key, &val); if(!status.ok()) std::cout<<status.ToString()<<std::endl; std::cout<<key.ToString()<<val<<std::endl; delete db; return(0); }
実行してみます.
> env LD_LIBRARY_PATH=/usr/local/lib/gcc45/ ./put_leveldb
無言で終了.読みだしてみます.
> env LD_LIBRARY_PATH=/usr/local/lib/gcc45/ ./get_leveldb Hello,World
データベースのファイルはこんな感じに格納されています.
> ls -l /tmp/testdb/ total 18 -rw-r--r-- 1 hoge wheel 123 5月 31 23:15 000005.sst -rw-r--r-- 1 hoge wheel 123 5月 31 23:15 000008.sst -rw-r--r-- 1 hoge wheel 123 5月 31 23:15 000011.sst -rw-r--r-- 1 hoge wheel 123 5月 31 23:15 000014.sst -rw-r--r-- 1 hoge wheel 123 5月 31 23:19 000019.sst -rw-r--r-- 1 hoge wheel 0 5月 31 23:19 000020.log -rw-r--r-- 1 hoge wheel 0 5月 31 23:19 000021.sst -rw-r--r-- 1 hoge wheel 16 5月 31 23:19 CURRENT -rw-r--r-- 1 hoge wheel 0 5月 31 23:19 LOCK -rw-r--r-- 1 hoge wheel 484 5月 31 23:19 LOG -rw-r--r-- 1 hoge wheel 353 5月 31 23:19 LOG.old -rw-r--r-- 1 hoge wheel 220 5月 31 23:19 MANIFEST-000018
圧縮されているだけあってファイルサイズ小さいですね.まだデータが少ないから?
次は,まずはベンチマーク.Tokyo/Kyoto Cabinetやmemcachedとの速度比較ですね.それと前後して,Linuxでのインストールを成功させたいです.