mixiのアーキテクトである 平林幹雄さんの一連のソフト群は mikio ware と呼ばれ、筋(どの筋?)では有名です。 いろいろなものを作られているのですが、最近、富に注目を浴びているのは、 Tokyo Cabinet / Tokyo Tyrant と呼ばれる軽量データベース。
個人的にはSQLite3という、 組み込みリレーショナルデータベースを良く使っていたのだが、 いくつか問題にぶち当たっていて、これを解決する手段を探していたというわけ。
特に1番めの欠点を直そうとSQLite3を改造しようとして挫折した経験あり(笑) これができないとウチのような小規模Webページでも大変困ったことになるのです。
一方Tokyo Cabinetは、RDBMSではないのでテーブル同士のJOINなどはできないものの、
という素晴らしい特徴があります。なかなかいい感じではないですか。
TCの真骨頂は、カリカリにチューニングしたハッシュデータベース(PerlやRubyの連想配列のようなKey-Value-Storage)にあるらしいのですが、私はテーブルデータベースにしか興味がないのでこれを使ってみます。
テーブルデータベースは、名前の通り「表」です。
が、SQLのテーブルとはだいぶ異なります。なんとカラムは自由に増やせます。特定の行だけに カラム'type'があって、他の行にはない、なんて事も可能です。SQLだとALTERで増やして、 デフォルト値で埋めるなんて作業をしなければなりませんが....
どうもTTは基本的にハッシュデータベースであり、 primary keyだけは特別 扱い しているものの、残りのカラムはすべてセルデータに結合して記憶されているようですね。 セルに「7」という数値だけではなく「type:7」という形で記憶されるようです。 事前にテーブル設計をしなくても良いのはメリットである半面、「typ o 」みたいな 間違いをしてもTT側でチェックしてくれる訳ではないのでハマりそうですな(笑)
あと、これは面喰らったのですが、サーバ版のTokyoTyrantでは基本的に 1つの初期化スクリプト=1データベース=1テーブルのようです。1データベースに 複数のテーブルを格納することはできないようです。つまりTCPポートが別々に分かれた 複数のサーバを起動しておくという使い方になるようです(少なくとも標準では)。
起動にはttserverというサンプルファイルを/etc/init.dにpostepgみたいな テーブル名と同じファイル名にしてコピーして所定の変数を書き換えます。 以下の例はポート1993にpostepg.tctという名前のテーブルを作成する例です。
# configuration variables prog="ttservctl" cmd="ttserver" basedir="/mnt/windex/postepg" port="1993" pidfile="$basedir/pid" #logfile="$basedir/log" #ulogdir="$basedir/ulog" ulimsiz="256m" #sid=2 #mhost="" #mport="1993" #rtsfile="$basedir/rts" dbname="$basedir/postepg.tct#opts=l#apow=8#dfunit=2" maxcon="1024"
chmod a+x postepgして/etc/init.d/postepgで起動します。
Perl,Rubyなどのバインディングが存在しますが、私はPerlを使いました。
use TokyoTyrant; my $pe = TokyoTyrant::RDBTBL->new(); die "ERROR: unable to connect" if (!$pe->open('localhost', 1993)); if ($pe->rnum == 0) { $pe->setindex($pec->{'eventid'}, $pe->ITDECIMAL); $pe->setindex($pec->{'nibble'}, $pe->ITTOKEN); $pe->setindex($pec->{'provider'}, $pe->ITLEXICAL); $pe->setindex($pec->{'title'}, $pe->ITQGRAM); } $pe->close();
ちょっとハマったのは データベースのタイプを合わせること ですか。hogehoge .tct をデプロイしている サーバにアクセスするためには、必ずTokyoTyrant::RDBTBLを使わねばなりません。 まあ少し考えればわかることですが...
登録数が0のときにインデックスを張っています。数値はITDECIMAL、文字列辞書式順はITLEXICAL、 「ニュース,報道」みたいなカンマや空白で区切られた複数の意味を持つ文字列はITTOKEN、 自由な単語で検索したい文字列にはITQGRAMを付与します。これで検索などが高速化されます。
データの登録はprimary keyを取得して、それに対してPerlのハッシュリファレンスをputします。
my $cols = { 'eventid'=>23435, 'nibble'=>'2f 情報・ワイドショー その他', 'provider'=>'NHK総合', 'title'=>'おはよう日本' }; my $rkey = $pe->genuid(); $pe->put($rkey, $cols);
検索結果はprimary keyのリストで返ってくるので、それを使ってgetします。 Q-GRAMのインデックスを張っておくと、SQLite3のlikeなんて目じゃない柔軟な検索が可能になります。 ちなみにQCFTSANDは空白で区切られた単語単位のAND検索。Googleとまではいかないけど、 こんなお手軽に柔軟な検索ができるようになるのはTTのメリットでしょう。
my $qry = TokyoTyrant::RDBQRY->new($pe); $qry->addcond('title', $qry->QCFTSAND, 'よう 本'); my @rlist = $qry->search(); foreach my $rkey (@rlist) { my $rcols = $pe->get($rkey); print $rcols->{'title'} . "\n"; }
基本はこれだけ。
ベンチマーク用にtcttestというコマンドが用意されているので、どのくらい速いか調べてみた。 インデックスは張りまくりの条件です。
tcttest write -tl -ip -is -in -it -if -ix ./test/test.tct
Q-GRAM転置インデックス生成を含む6種類のインデックスを張りながらでも、 250万レコード平均で4800レコード/秒というすさまじい速度です。 100万レコードから250万まで測定しましたが、微妙に時間が延びるもののほぼ線形。 ディスクの延びもほぼ線形でした。
最近はスケールアウトが流行りですが、これだけ速ければTC一つで事足りる事例も 多いのではないでしょうか。特に個人では.....
次は検索時間を調べてみたいと思います。