テーブル型のストレージをサポートし、制限はあるもののRDBMSと似たような感じに使える(はず)の Tokyo Cabinet。 実際にデータを登録して性能を見てみました。
まず生成条件。64bit有効でアライメント力256バイト(2^8)。CabinetではなくTyrantで接続。
dbname="$basedir/chest.tct#opts=l#apow=8#dfunit=2"
1行当たりのデータは....ちょっと仕事のデータを使ってしまったので(ぉ) 具体的には説明できないのですが、整数型x8,文字列型x3です。文字列型(UTF-8)で 1行当たりの平均バイト700バイト程度、最大2.1KBです(*けっこう大きい)。 また、整数カラム5つの完全一致でUNIQUEな制約がついています。SQL風に書くと(仮)、
UNIQUE(emptype, bumonid, groupid, nyuusyanendo, zyugyoincode);
みたいな感じです。上記の条件が全部一致した行は重複して登録したくない、ということです。 このデータを571265行登録してみました。
登録条件は以下の通り。
結果は以下の通り。横軸は1万行ごと、縦軸は1秒当たりの処理行数です。
既存テーブルを検索してみて無かったら新規登録する という処理は非常にありがちだと 思うのですが、ちょっと実用にならないくらい遅い....あまりの遅さに断念しました。
そうでした。KVSは基本ハッシュデータベースなので、主キーの取り方で制約を実現できるのです。 つまり、ユニークIDをもらってくるこういう関数よりも、
my $id = $jinji->genuid();
どうせ文字列も使えるのだからキーをsprintfか何かで生成してしまえばよいのだと。
my $id = sprintf("%d_%d_%d_%d_%d", $emptype, $bumonid, $groupid, $nyuusyanendo, $zyugyoincode);
これなら被ることはないし、被った場合は後から来たデータで上書きされるだけ。 もちろん「登録しない」ということと「上書き」は全く異なる処理だけど、 今回の用途では問題ないのでやってみた。
通常の連想配列の実装では、ハッシュ関数を通すオーバーヘッドはあるはずだが、 キーの長さはほぼ一定なのだから処理時間も一定のはず、なんだけどなー。 これもあまりに遅いので打ち切った。
後から来たデータで更新するのを抑止するにはputkeep()関数を使えばよいようです (ニューレコードのみ登録する)。 現実には上記のテストデータは全てユニークだから、UNIQUE制約にひっかかることはないので、 put()関数と違いはないと思っていたのですが....
やっぱり変わりません。 次いこ次。
という推測も成り立つ。テーブルデータベースは、 ハッシュテーブルの各要素にBtreeのポインタが入ってる....という実装なのかなあ。 ってことは、ハッシュテーブルが疎になるような設定をしなければならないんだろう。 ざっと基本仕様書読んでbnumは関係ないと勝手に思い込んでいた。 57万件ということは2倍の100万件くらいにしておけばいいかな。
dbname="$basedir/chest.tct#opts=l#bnum=1000000#apow=8#dfunit=2"
やっぱり変わりません。 万策尽きてきた.....
基本に立ち戻ってつらつらと.....
bool tctdbsetindex(TCTDB *tdb, const char *name, int type); `tdb' specifies the table database object connected as a writer. `name' specifies the name of a column. If the name of an existing index is specified, the index is rebuilt. An empty string means the primary key.
え? An empty string means the primary key. ?? ということはデフォルトでは主キーにもインデックス貼られてないの??? そんなバカな....
$jinji->setindex('', $pm->ITLEXICAL);
を追加。
俺にはTokyo Cabinetは使いこなせないようです。
いままでやってたの全部的外れ!!!!
お気軽全文検索 をやりたかったので安易にQ-GRAMインデックスを張っていたのだが.....仇になった。
以下のサンプルで再現できる。
tyrant.plはttserverへ接続してテーブルを登録する。キーは数値。 重複チェックはttserverでおまかせ。1行登録するたびに、 original.txtから1行読んで全文検索のインデックスを貼る、というのが サンプルのアルゴリズム。
しかし、このoriginal.txtが添付の数行みたいなものであれば問題ないのだが.... 多彩な文字の組み合わせを含むソースだとバカみたいに素片量が増えてしまい、 登録時間もうなぎ登りになってしまうようだ。手元では2ちゃんねるのdatログを 数十個結合したもの差し替えてみた。つまりヘッダありAAありというテキスト行が3万行 くらい続いているわけ。これで全文検索のインデックスを作成すると、 毎回確実に適当な単語が追加されていく。
グラフの横軸は1万件ごと、縦軸は1万件を登録するのにかかった秒数。
ファイルサイズを見てみよう。
-rw-r--r-- 1 root root 125237504 2010-03-23 19:18 testdb.tct -rw-r--r-- 1 root root 111360 2010-03-23 19:06 testdb.tct.idx.10.dec -rw-r--r-- 1 root root 23888640 2010-03-23 19:18 testdb.tct.idx.11.dec -rw-r--r-- 1 root root 111360 2010-03-23 19:06 testdb.tct.idx.2.dec -rw-r--r-- 1 root root 14170624 2010-03-23 19:18 testdb.tct.idx.3.dec -rw-r--r-- 1 root root 534364416 2010-03-23 19:18 testdb.tct.idx.6.qgr ※これ!! -rw-r--r-- 1 root root 111360 2010-03-23 19:06 testdb.tct.idx.7.dec -rw-r--r-- 1 root root 111360 2010-03-23 19:06 testdb.tct.idx.8.dec -rw-r--r-- 1 root root 111360 2010-03-23 19:06 testdb.tct.idx.9.dec
これはひどい......... 本文が格納されているであろうtestdb.tctよりはるかにデカイ... 登録時間を一定にするのは難しいんだろうか??できれば単語の種類の量じゃなくて、 毎回登録する1行のテキストの長さだけに処理量が比例して欲しいんだけど.... でも俺が同じもの作ったらさらに1/100くらい遅いだろうしなあウムム.....
まあ、適材適所というか、Q-GRAMは使わないほうがいいですね。