Berkeley DB v.s Tokyo Cabinet & QDBM

[ガラケー版(QRコード)] 総閲覧回数:3,773,750回 / ブログ拍手:2,351
作品DB等各サービスの機能追加情報や、技術系・面白系記事を中心に提供。
記事の投稿は基本Twitterでも告知させて頂いています。
連絡は作品DBの論客の方なら私書、DB外ユーザの方ならメールTwitterで可能です。
アクセス記録[推移 / PV内訳(過去1日 / 過去1週間) / 外部アクセス元 (昨日 / 過去1週間) / ログイン論客足跡]
プロフィール私書(メール)
   /   /送済
評価(一覧   /)
投票   /共:   /
ファン登録
作品/情報/
DB構築()
ブログ
[書く]
攻略記事リンク集
My Play List
<=次の記事 終了::「ゆびとま」もやばい?
=>前の記事 信頼性::サイトの実際のトラフィックとALEXAの相関関係(信頼性について)

1.
2009/05/06 同日2番目 Berkeley DB > 比較 > Berkeley DB v.s Tokyo Cabinet & QDBM」
[この書込みのみ表示(記事URL紹介用) / 編集 / 削除 / トラバ送信 / 共有分類に追加(タグ付け)]拍手:4個

1. 前書き
    1. Web+DB Press Vol50
    2. インストール手順
2. 比較テスト
    1. DBMの比較表の出元
    2. 元々のデータ
    3. 元々のテストを手元の環境で実行してみたもの
    4. 動作を遅くする方向に作用しているチューニングの取り消し
    5. /dev/shm上での実行
    6. 変更した結果
    7. Perl & もっと大きなデータでの比較
    8. テストコード

1. 前書き


    1. Web+DB Press Vol50

Web+DB Pressというインターネットの開発者向けの雑誌があるのですが、その50号
特集3
Webアプリのパフォーマンス向上の一策
[旬のライブラリ大集合]key-valueストア入門 
という記事が載っていた。
その中で他DBMと比較してtokyo cabinetの速度が速いという表が出されていた。
記事の著者は末永匡さんだが、その表の出元はtokyo cabinetの作者の平林幹雄さんのテスト結果。
末永匡さんはsennaの作者の方、平林幹雄さんはHyper Estraierの作者方、と、自分も一応そうですが、速度を気にする検索エンジン開発者の方はkey/value型に辿り着き易いのだろうなぁ、と思いつつ眺める。
両方共OSSとニコニコ、mixiというウェブサービスの両方に大きく貢献されている所謂世の役に立つサービスを出されている方々です。
で、tokyo cabinetの数値がその表では速度と生成されるファイルサイズという観点でかなり良い数値で、こんなに良いのなら自分も使う事を検討する位興味深いと思ったのですが、昔速いと聞いて自分のテスト条件で比較してみた時は、QDBMはBerkeley DBより結果が悪かったので、この記事のデータも自分で検証した方が良いだろうと思い、検証してみました。
    2. インストール手順

まずtokyo cabinetとQDBMをインストール。
wget http://tokyocabinet.sourceforge.net/tokyocabinet-1.4.18.tar.gz;
tar xvfz tokyocabinet-1.4.18.tar.gz;
cd tokyocabinet-1.4.18;
./configure;
make;
make install;
cd ..;

wget http://qdbm.sourceforge.net/qdbm-1.8.77.tar.gz;
tar xvfz qdbm-1.8.77.tar.gz;
cd qdbm-1.8.77;
./configure;
make;
make install;
cd ..;

Berkeley DBのインストールについてはこちら。
http://www.accessup.org/pj/6_B4C9CDFDBFCDA4B5A4F3/13/6_A5A4A5F3A5B9A5C8A1BCA5EB/
2. 比較テスト


    1. DBMの比較表の出元

tokyo cabinetをダウンロードしてみて気付いたのだが、各DBMのパフォーマンス比較はtokyo cabinetについてくるbrosディレクトリ以下で
make all;
でプログラムを作り、./reportを実行することで得られる結果を元にしているようだ。
これはこっちでテスト結果を確認するには便利ですね。
テストの内容はkey, valueそれぞれに00000001から01000000までの値を入れるということを、ハッシュ、BTREE順番(ASC)、BTREEランダム(RND)の3パターンでやるというもの。
・かなり小さいサイズの挿入
・keyの挿入の仕方が特に順番に入れていく場合最小限の差
という点がかなり特徴的。
    2. 元々のデータ

まず元々その雑誌に載っていて、現在も付属しているもの。
NAMEDESCRIPTIONWRITE TIMEREAD TIMEFILE SIZE
TC-HASHTokyo Cabinet 1.3.50.402 0.334 42,583,208 
QDBM-HASHQuick Database Manager 1.8.772.779 0.962 56,582,932 
BDB-HASHBerkeley DB 4.6.219.108 3.109 41,938,944 
TC-BT-ASCB+ tree API of TC (ascending order)0.707 0.601 32,340,739 
TC-BT-RNDB+ tree API of TC (at random)2.896 2.263 12,532,397 
QDBM-BT-ASCB+ tree API of QDBM (ascending order)1.404 0.951 40,620,715 
QDBM-BT-RNDB+ tree API of QDBM (at random)6.311 3.816 15,731,675 
BDB-BT-ASCB+ tree API of BDB (ascending order)1.354 1.451 57,999,360 
BDB-BT-RNDB+ tree API of BDB (at random)3.751 2.415 29,818,880 

    3. 元々のテストを手元の環境で実行してみたもの

   
手元のテスト環境
筐体Dell PowerEdge 860
OSCent OS5.3
CPUIntel Xeon X3220 2.40GHz(4コア)
メモリー8GB
HDDSeagate146GB(15000rpm / SAS / RAID 1)

手元のテスト環境でのそのままのテストケース実行
NAMEDESCRIPTIONWRITE TIMEREAD TIMEFILE SIZE
TC-HASHTokyo Cabinet 1.3.5 => 1.4.180.365010.3296842583208
QDBM-HASHQuick Database Manager 1.8.773.028381.3925356582932
BDB-HASHBerkeley DB 4.6.21 => 4.7.256.440032.1773941938944
TC-BT-ASCB+ tree API of TC (ascending order)0.627790.5582832,340,739 => 19806668
TC-BT-RNDB+ tree API of TC (at random)2.632432.0424212531710
QDBM-BT-ASCB+ tree API of QDBM (ascending order)1.425650.9503540,620,715 => 24889040
QDBM-BT-RNDB+ tree API of QDBM (at random)5.739953.37515731675
BDB-BT-ASCB+ tree API of BDB (ascending order)1.941751.4697857,999,360 => 28180480
BDB-BT-RNDB+ tree API of BDB (at random)3.945682.2265429818880

Berkeley DBとTokyo Cabinetは雑誌に載っているテストケースの時より新しい版を使ってのテストになっていますが、BTREEのASCEND(順列)で出来るファイルサイズが雑誌に載っている物と、手元で実行した結果とでは無視出来ない位大きく違っていますね。
実行環境によるものなのかどうか分かりませんが(TC, BDBはバージョンの差というのがありえますがQDBMは同じバージョンの筈)、雑誌・サイトに掲載されているものよりも、こちらの手元で実行した結果の方が一般的な結果だと思われます。
少なくとも現行のバージョンでは。
なので、元々の表ほど順番に入れる方がランダムに入れるよりも大きなファイルサイズになるということはないと思います(Berkeley DBでは順番に作った方が寧ろ小さくなっている)。
    4. 動作を遅くする方向に作用しているチューニングの取り消し

注釈を見ると、パラメーターをチューニングしてあるとのこと。
とりあえず少しはチューニングパラメーターが分かるBerkeley DBのテストコードを覗く。

...
#define SMALL_PAGESIZE  512              /* small page size */
...
  if(dbp->set_pagesize(dbp, SMALL_PAGESIZE) != 0){
    fprintf(stderr, "DB->set_pagesize failed\n");
    dbp->close(dbp, 0);
    return 1;
  }
ああ、このシステムのPAGE_SIZEよりも小さいものをセットするという設定では、チューニングどころか逆にパフォーマンスを劣化させてしまう。
参考:Berkeley DB Reference Guide: Selecting a page size
...
Selecting a database page size smaller than the filesystem block size 
may cause the operating system to coalesce 
or otherwise manipulate 
Berkeley DB pages and can impact the performance of your application. 
When the page size is smaller than the filesystem block size and a page 
written by Berkeley DB is not found in the operating system's cache, 
the operating system may be forced to read a block from the disk, 
copy the page into the block it read, and then write out the block to disk,
 rather than simply writing the page to disk. 
Additionally, as the operating system is reading more data into its buffer cache 
than is strictly necessary to satisfy each Berkeley DB request for a page, 
the operating system buffer cache may be wasting memory.
...
ということでここを削る。
そして修正したものを走らせた結果
NAMEDESCRIPTIONWRITE TIMEREAD TIMEFILE SIZE
BDB-HASHBerkeley DB 4.7.256.44003 => 3.581452.17739 => 1.6805641938944 => 41582592
「チューニング」を解除することで書き込みが1.8倍速、読み込みが1.29倍速、そしてファイルサイズがもうちょっと縮小しました。
    5. /dev/shm上での実行

↑でチューニングの筈が逆に遅い結果を出させてしまっていたように、中々自分の範囲外のところのチューニングをするのは難しいですね。
DBM系はチューニングで劇的に性能が変わったりしますが、API情報はあってもその実際の活用方法のノウハウがあまり公開されていないのが難点です。
とりあえず、後あるパラメーターで大きいものの一つとしては、全ての処理をHDDでIOさせずにするようにするかしないかというのがあります。
処理時間の大きな差は、HDDのIO量の差で生まれるということがよくあります。
そこらへんの比較を手軽にする為/dev/shm/上でやってみました。

NAMEDESCRIPTIONWRITE TIMEREAD TIMEFILE SIZE
TC-HASHTokyo Cabinet 1.4.180.33810.3296742583208
QDBM-HASHQuick Database Manager1.8.773.02838 => 1.595731.3556656582932
BDB-HASHBerkeley DB 4.7.253.58145 => 2.612451.6532541582592
TC-BT-ASCB+ tree API of TC (ascending order)0.626010.5678419806668
TC-BT-RNDB+ tree API of TC (at random)2.628962.0344512531710
QDBM-BT-ASCB+ tree API of QDBM (ascending order)1.304960.9548324889040
QDBM-BT-RNDB+ tree API of QDBM (at random)5.641233.361515731675
BDB-BT-ASCB+ tree API of BDB (ascending order)1.94175 => 1.15191.6058128196864
BDB-BT-RNDB+ tree API of BDB (at random)3.94568 => 2.9152.3644629818880 => 27803648

BDB系の書き込みが高速化しています。
TCは全くその恩恵が無く、QDBMも一部に限られるようです。
元々HDDのIOをこの程度では生成途中で使わずメモリーの利用で完結するようにほぼチューニングorそういうロジックになっているということでしょう。
    6. 変更した結果

本当はBDBのHashはoverflowが発生している分set_h_nelemでハッシュテーブルのサイズを先に定義してあげることなどでまだチューニング出来る筈ですが、自分の利用範囲だとBDBはBtreeで使うのが主で、Hashの方はチューニングの知識に欠けるのでとりあえずここまでの結果で比較。

簡単な比較ですが、最初のものでは
Hashの場合
tokyo cabinet >>> QDBM >>> BDB
だったのが
tokyo cabinet >>> QDBM > BDB
になりました。

Btree ASCの場合
tokyo cabinet >>> BDB = QDBM
だったのが
tokyo cabinet > BDB = QDBM
になりました。

Btree Randの場合
tokyo cabinet > BDB > QDBM
だったのが
tokyo cabinet > BDB > QDBM (TCとBDBの間は0.3秒にまで縮小)
になりました。

値的には結構変化はありましたが、序列の変更にまでには至らず、ってとこですね。
但し、上記のテストには欠けているものがあり(値が長いものを格納した場合 & 更新した場合)、これだけではこのケースがTCとQDBMに最適なのではないか?という疑問も生じてしまうと思います。
    7. Perl & もっと大きなデータでの比較

本来はperlではなくCで比較した方が良いのですが、自分の方でDBMの比較用に作ってあったのがperlなので、簡易的にperlで比較。
こちらのテストケースの特徴は、値に比較的大きめな値を入れる、ということです。
「全て値がaで8124個分入ったのデータを、10万回一つのファイルに書き込み、その値を全て更新し、その値を全て読み込むという処理を行う。」
という処理を行っています。
テストはHDD上でテストしています。
使うモジュールはtokyo cabinetついては公式のもの、Berkeley DBについてはBerkeleyDB.pmを使っています。
QDBM.pmについては、インストールしようとしたらエラーが現環境では出てきたので、面倒なのでスキップさせて頂きました。
テストは別として、実用的にはTokyo cabinetはQDBMの後継系統なので、QDBMの方を使う理由はないので。

perl bench_bdb.pl --cache=1000000000 --type=tc
Write   56 wallclock secs ( 1.88 usr +  2.32 sys =  4.20 CPU)
Update  21 wallclock secs ( 2.09 usr +  1.67 sys =  3.76 CPU)
Read     2 wallclock secs ( 1.37 usr +  0.72 sys =  2.09 CPU)

Write   24 wallclock secs ( 1.92 usr +  2.19 sys =  4.11 CPU)
Update  19 wallclock secs ( 2.12 usr +  1.63 sys =  3.75 CPU)
Read     2 wallclock secs ( 1.43 usr +  0.71 sys =  2.14 CPU)

Write    4 wallclock secs ( 1.88 usr +  2.19 sys =  4.07 CPU)
Update  18 wallclock secs ( 2.15 usr +  1.52 sys =  3.67 CPU)
Read     2 wallclock secs ( 1.39 usr +  0.72 sys =  2.11 CPU)

Write   14 wallclock secs ( 1.87 usr +  2.33 sys =  4.20 CPU)
Update  40 wallclock secs ( 2.10 usr +  1.57 sys =  3.67 CPU)
Read     2 wallclock secs ( 1.42 usr +  0.71 sys =  2.13 CPU)

Write   54 wallclock secs ( 1.89 usr +  2.29 sys =  4.18 CPU)
Update  32 wallclock secs ( 2.11 usr +  1.64 sys =  3.75 CPU)
Read     2 wallclock secs ( 1.34 usr +  0.77 sys =  2.11 CPU)
理由は不明ですが、他に稼動している処理が無い状況でも、tokyo cabinetのperl経由での利用はかなり実行時間がランダムで、遅い時には最速の時に比べ相当な時間がかかっています。

次にBerkeley DBでの実行結果。
perl bench_bdb.pl --cache=1000000000 
Write   17 wallclock secs ( 1.83 usr +  3.11 sys =  4.94 CPU)
Update  15 wallclock secs ( 2.10 usr +  1.76 sys =  3.86 CPU)
Read     1 wallclock secs ( 1.13 usr +  0.33 sys =  1.46 CPU)

Write   17 wallclock secs ( 1.84 usr +  3.09 sys =  4.93 CPU)
Update  15 wallclock secs ( 2.18 usr +  1.85 sys =  4.03 CPU)
Read     1 wallclock secs ( 1.11 usr +  0.35 sys =  1.46 CPU)

Write   17 wallclock secs ( 1.80 usr +  3.08 sys =  4.88 CPU)
Update  16 wallclock secs ( 2.19 usr +  1.72 sys =  3.91 CPU)
Read     1 wallclock secs ( 1.02 usr +  0.45 sys =  1.47 CPU)

Write   16 wallclock secs ( 1.80 usr +  3.06 sys =  4.86 CPU)
Update  16 wallclock secs ( 2.15 usr +  1.70 sys =  3.85 CPU)
Read     1 wallclock secs ( 1.18 usr +  0.26 sys =  1.44 CPU)

Write   16 wallclock secs ( 1.85 usr +  3.03 sys =  4.88 CPU)
Update  18 wallclock secs ( 2.26 usr +  1.74 sys =  4.00 CPU)
Read     1 wallclock secs ( 1.06 usr +  0.39 sys =  1.45 CPU)
実行する度に大きく実行時間が変わることがなく、安定しています。

また、結果として出来るファイルのサイズも
Berkeley DB: 823MB
Tokyo cabinet: 827MB
と、一応BDBの方が小さいです。

こっちのテストケースを見ると、ワーストケース・中央値・ファイルサイズから
Berkeley DB > tokyo cabinet
と結論付けてしまいそうになりますが、要するに自分の使う条件で比較してみる事が、何であれ重要だということです。

マイクロソフトがLinux/Firefoxなどに対してWindows/IEの優秀さを宣伝する広告に簡単に乗っかってはいけないように、その製品の利害関係者のテスト結果をそのまま受け入れるのは危険です。
意図するにせよ、意図しないにせよ、テストというのはその製品にとって最適な条件で実行される事はよくあることです。
また、最適なチューニングも、自分の製品に対してはされていても、他者の製品に対して中々出来ないものです。

そうでなくても他人のテストケースというのは、自分の実行状況とは異なるものなので、簡単に乗っかってはいけません。
この私のテストケースの場合も、Tokyo cabinetに対するチューニングも作者に比べると不十分な可能性が多々あります。
重要なことならば、必ず自分の条件でテストしてみましょう。

いずれにせよ、自分の利用ケースの場合には、乗り換えるべきものとは見えないけれども、元々提供されているテストケースの条件下ではかなり優秀な成績だったのは確かなので、今後その特性の研究させて頂く価値はありそうだな、ということは分かりました。
コード量も比較的小さめなので、頑張ればソースを読むことも出来そうです。
    8. テストコード

上記のテストの為に作ったコード。  

#!/usr/bin/perl -w
package bench_bdb;

=pod
Copryright: 1st class, inc.
Script by: Hajime Kurita
URL: http://www.accessup.org/

Preparation: 
perl -MCPAN -e shell "install BerkeleyDB";
perl -MCPAN -e shell "install BDB::Wrapper"; # This module is distributed at http://search.cpan.org/~hikarine/ (Author of this script)

Purpose:
Check the speed of hdd
Check how much cache is best for the BDB
Check whether hash or btree is good for your system
Check the difference of the speed of each version of BerkeleyDB
=cut

use strict;
use BDB::Wrapper;
use TokyoCabinet;
use Benchmark;

my $pro=new bench_bdb;
$pro->run();

sub new(){
  my $self={};
  return bless $self;
}

sub run(){
  my $self=shift;
  $self->init_vars();
  $self->check_bdb_speed();
  $self->report();
}

sub init_vars(){
  my $self=shift;
  $self->{'hash'}=$self->{'max'}=0;
  my $cache='';
  my $no_lock=0;
  my $ram=0;
  my $len=8124;
  $self->{'type'}='';
  foreach my $ARGV (@ARGV){
    if($ARGV=~ m!^--max=(\d+)!){
      $self->{'max'}=$1;
    }
    elsif($ARGV=~ m!^--hash!){
      $self->{'hash'}=1;
    }
    elsif($ARGV=~ m!^--ram=(\d+)!){
      $ram=$1;
    }
    elsif($ARGV=~ m!^--len=(\d+)!){
      $len=$1;
    }
    elsif($ARGV=~ m!^--cache=(\d+)!){
      $cache=$1;
      if($cache>1000000000){
        print "To avoide system failure by mistype, you cannot set the cache which is over than 1GB.";
        print "You tried to set ".$self->add_digit($cache)." bytes. If you really want to do it, please modify this script by your hand.";
        exit;
      }
    }
    elsif($ARGV=~ m!^--no_lock=(\d+)!){
      $no_lock=$1;
    }
    elsif($ARGV=~ m!^--type=(.+)$!){
      $self->{'type'}=$1;
    }
    else{
      die "Invalid option";
    }
  }
  
  $self->{'put_str'}='';
  for(my $i=0;$i<$len;$i++){
    $self->{'put_str'}.='a';
  }
  
  $self->{'max'}=100000 unless $self->{'max'};
  my $pwd=`pwd`;
  $pwd=~ s!\s!!gs;
  $self->{'bdb'}=$pwd.'/speed.bdb';
  $self->{'bdb'}.='.'.$self->{'type'} if($self->{'type'});
  unlink $self->{'bdb'} if -f $self->{'bdb'};
  if($self->{'type'}){
    $self->{'tch'}=TokyoCabinet::BDB->new();
    
    # tchdbtune(hdb, rnum * 3, 0, 0, 0);
    $self->{'tch'}->tchdbsetxmsiz($cache) if $self->{'cache'};

  }
  else{
    system('rm -rf /dev/shm/bdb_home'.$pwd.'/speed');
    system('rm -rf /tmp/bdb_home'.$pwd.'/speed');
    print 'no_lock='.$no_lock.' ram='.$ram.' cache='.$cache."\n";
    $self->{'bdbw'}=new BDB::Wrapper({'no_lock'=>$no_lock, 'ram'=>$ram, 'cache'=>$cache});
  }
  $self->{'time'}={};
}

sub check_bdb_speed(){
  my $self=shift;
  $self->check_time_to_write();
  $self->check_time_to_update();
  $self->check_time_to_read();
}

sub check_time_to_write(){
  my $self=shift;
  my $start=new Benchmark;
  if($self->{'type'}){
    if(!$self->{'tch'}->open($self->{'bdb'}, $self->{'tch'}->OWRITER | $self->{'tch'}->OCREAT)){
      my $ecode = $self->{'tch'}->ecode();
      printf STDERR ("open error: %s\n", $self->{'tch'}->errmsg($ecode));
    }
    # store records
    for(my $i=0;$i<$self->{'max'}+1;$i++){
      if(!$self->{'tch'}->put($i, $self->{'put_str'})){
        my $ecode = $self->{'tch'}->ecode();
        printf STDERR ("put error: %s\n", $self->{'tch'}->errmsg($ecode));
      }
    }
    if(!$self->{'tch'}->close()){
      my $ecode = $self->{'tch'}->ecode();
      printf STDERR ("close error: %s\n", $self->{'tch'}->errmsg($ecode));
    }
  }
  else{
    my $bdbh=$self->{'bdbw'}->create_write_dbh($self->{'bdb'}, {'hash'=>$self->{'hash'}}) or die "Failed to write to ".$self->{'bdb'}."\n";
    for(my $i=0;$i<$self->{'max'}+1;$i++){
      unless($bdbh->db_put($i, $self->{'put_str'})==0){
        die "Failed to write to ".$self->{'bdb'};
      }
    }
    $bdbh->db_close();
  }
  
  my $end=new Benchmark;
  $self->{'time'}->{'write'}=timestr(timediff($end, $start));
  print "Write\t".$self->{'time'}->{'write'}."\n";
}

sub check_time_to_update(){
  my $self=shift;
  my $start=new Benchmark;
  if($self->{'type'}){
    if(!$self->{'tch'}->open($self->{'bdb'}, $self->{'tch'}->OWRITER | $self->{'tch'}->OCREAT)){
      my $ecode = $self->{'tch'}->ecode();
      printf STDERR ("open error: %s\n", $self->{'tch'}->errmsg($ecode));
    }
    # store records
    for(my $i=0;$i<$self->{'max'}+1;$i++){
      if(!$self->{'tch'}->put($i, $self->{'put_str'})){
        my $ecode = $self->{'tch'}->ecode();
        printf STDERR ("put error: %s\n", $self->{'tch'}->errmsg($ecode));
      }
    }
    if(!$self->{'tch'}->close()){
      my $ecode = $self->{'tch'}->ecode();
      printf STDERR ("close error: %s\n", $self->{'tch'}->errmsg($ecode));
    }
  }
  else{
    my $bdbh=$self->{'bdbw'}->create_write_dbh($self->{'bdb'}, {'hash'=>$self->{'hash'}}) or die "Failed to write to ".$self->{'bdb'}."\n";
    for(my $i=0;$i<$self->{'max'}+1;$i++){
      unless($bdbh->db_put($i, $self->{'put_str'})==0){
        $bdbh->db_close();
        die "Failed to update ".$self->{'bdb'};
      }
    }
    $bdbh->db_close();
  }
  my $end=new Benchmark;
  $self->{'time'}->{'update'}=timestr(timediff($end, $start));
  print "Update\t".$self->{'time'}->{'update'}."\n";
}

sub check_time_to_read(){
  my $self=shift;
  my $str='';
  my $start=new Benchmark;
  if($self->{'type'}){
    if(!$self->{'tch'}->open($self->{'bdb'}, $self->{'tch'}->OREADER)){
      my $ecode = $self->{'tch'}->ecode();
      printf STDERR ("open error: %s\n", $self->{'tch'}->errmsg($ecode));
    }
    # store records
    for(my $i=0;$i<$self->{'max'}+1;$i++){
      $str=$self->{'tch'}->get($i);
      unless(defined($str)){
        my $ecode = $self->{'tch'}->ecode();
        printf STDERR ("get error: %s\n", $self->{'tch'}->errmsg($ecode));
      }
    }
    if(!$self->{'tch'}->close()){
      my $ecode = $self->{'tch'}->ecode();
      printf STDERR ("close error: %s\n", $self->{'tch'}->errmsg($ecode));
    }
  }
  else{
    my $bdbh=$self->{'bdbw'}->create_read_dbh($self->{'bdb'}, {'hash'=>$self->{'hash'}}) or die "Cannot open file 'fruit': $!\n";
    for(my $i=0;$i<$self->{'max'}+1;$i++){
      unless($bdbh->db_get($i,$str)==0){
        $bdbh->db_close();
        die "Failed to read ".$self->{'bdb'};
      }
    }
    $bdbh->db_close();
  }
  my $end=new Benchmark;
  $self->{'time'}->{'read'}=timestr(timediff($end, $start));
  print "Read\t".$self->{'time'}->{'read'}."\n";
}

sub report(){
  my $self=shift;
  foreach my $key (reverse sort keys %{$self->{'time'}}){
    print $key."\t";
  }
  print "\n";
  foreach my $key (reverse sort keys %{$self->{'time'}}){
    my $time=$self->{'time'}->{$key}."\t";
    $time=~ s!\swall.*!!;
    print $time."\t";
  }
  print "\n\n";
  my $size=-s $self->{'bdb'};
  print 'Size'."\t".$self->add_digit($size)."\n";
}

sub add_digit{
  my $self=shift;
  my $number=shift;
  my $keta_num=0;
  my $kugiri='';
  my @te=();
  my $pm='';
  my $sita='';
  
  if($number=~ m!^([\+\-])?\d+(\.\d+)?$!){
    $pm=$1 || '';
    $sita=$2 || '';#. must be in this
    unless(length($sita)){
      $sita='';
    }
    if($pm ne ''){
      $number=~ s!^[\+\-]!!;
    }
    if($sita ne ''){
      $number=~ s!\.\d+$!!;
    }
  }
  else{
    return undef;
  }
  
  $keta_num=3;
  $kugiri = ',';
  
  my $m='';
  my $kazu = 0;
  my $len = length($number);
  if($len != 0){
    $kazu = sprintf("%.0f",$len / $keta_num);
    my $amari = $len % $keta_num;
    if(0 > $len - $kazu * $keta_num){
      $kazu--;
    }
    if(0 == $len % $keta_num){
      $kazu--;
      $amari = $keta_num;
    }
    if($kazu != 0){
      for(my $i=0;$i<$kazu+1;$i++){
        if($i == $kazu){
          $te[$i] = substr($number ,-$keta_num * $i - $amari,$amari);
        }
        else{
          $te[$i] = substr($number ,-$keta_num * ($i + 1),$keta_num);
        }
      }
      for(my $i=$kazu;$i > -1;$i--){
        if($i==0){
          $m = $m.$kugiri.$te[$i];
        }
        elsif($i==$kazu){
          $m = $te[$i];
        }
        else{
          $m = $m.$kugiri.$te[$i];
        }
      }
      $number=$m;
    }
  }
  if($pm ne ''){
    $number=$pm.$number;
  }
  if($sita ne ''){
    $number.=$sita;
  }
  return $number;
}


コメントする4個


管理人さん さんのコメント (2009/05/22) [編集/削除(書込み者/所有者が可能)]
グニャラくん さん、わざわざアカウントをとって頂いた上でのコメント、ありがとうございます&お手数お掛け致しました。
グニャラくんさんにこっちのngramの記事にリンク貼ってもらったり、楽天の某役員の方に検索エンジニアで知り合いがいるとぐにゃら君との飲み会に誘われたりで名前聞いた事あり、ということがあり、たまにブログも拝見させて頂いていたので、個人的にグニャラくんさんの名前買いでWeb+DB買わせて頂きました(^^

key+value型はよく使うということもありますが、個人的には凄く興味がある分野です。
世間的にはいまいちな興味の持たれ具合ですが、ぐにゃらくんさんや平林さんのOSS&啓蒙活動などで、日本でももっとその活用方法のノウハウ・知識が広まると良いなと思っています。
自分の場合は極端かもしれませんが、このサイトとかはSQL DBは使わずデータストアはBDBまたはテキスト&Binaryだけで作っています。
グニャラくん さんのコメント (2009/05/22) [編集/削除(書込み者/所有者が可能)]
どもー、WEB+DB press Vol.50でkey-valueストアの記事書いた末永です。
興味深く拝見させていただきました。

「要するに自分の使う条件で比較してみる事が、何であれ重要」
僕もそう思います。Perlなどの言語バインディングのデキによっても左右されますしね。
個人的には、「どれを使っても充分高速」だと思っています。

ベンチマークの類は一人歩きすることがあって危険です。
記事中で僕が主体的にベンチマークをとらずに他の方の結果を引用したのは、
利用者の多くのユースケースを包含するベンチマークを作るのが難しかったからです。


[他の記事も読む]
<=次の記事 終了::「ゆびとま」もやばい?
=>前の記事 信頼性::サイトの実際のトラフィックとALEXAの相関関係(信頼性について)


大分類が「Berkeley DB」の記事
この論客の記事全て
RSS購読
RSS
ブログ表示スタイル
リスト/携帯(QRコード)
画像/動画/音声/リンク
表示開始年月
分類
全て
1.このサイトについて
2.作品DB開発/運用
3.ホームページ制作技術
4.Perl
5.C言語 / C++
6.検索エンジン&SEO
7.サッカー
8.自分のこと
9.Linux
10.旅行
11.思ったこと
12.パソコン
13.Berkeley DB
14.その他技術系
15.企画
16.スマートフォン
17.鑑賞
18.皆声.jpニュース
19.インターネット業界
20.運用マニュアル(自分用)
21.技術系以外実用書
22.料理
23.ALEXA
24.アニメ
25.会計
26.漫画
27.設計書
28.色々サイト作成
29.サーバー
30.自分専用
31.生活
32.OP/ED/PV
33.ゲーム
34.DB整備
35.新規開始作品紹介
36.英語圏の話題
37.大道芸
38.映画
39.PHP
40.ダイエット
41.Mac
42.JavaScript
43.MySQL
44.介護
45.作品DB作品追加作業
46.BI
47.Web API
48.パフォーマンス
49.インターネットの活用方法
50.Riak
51.Androidアプリ開発
52.Cassandra
53.スパム
54.写真
55.iOSアプリ開発
56.AWS
57.マーケティング
58.Web漫画
59.法律
60.mongodb
61.開発環境整備
62.Google Apps Script
63.meteor
64.Pentaho
65.Ansible
66.VPS
67.技術書メモ
68.Vagrant
69.Docker
70.dokuwiki
71.Apple Watch
72.Webサービス
73.セキュリティ
74.Elastic Search
75.Wordpress
76.クラウド
77.英語
78.MVNO
79.シンガポール
80.マレーシア
81.海外生活
日記の主な内容
サイト運営/開発
検索エンジン情報
・技術ネタ(Berkeley DB,
Linux, Perl, サイト作成)等

サイト管理
全まとめ
サーバー管理
定期処理状況
開発予定
削除提案
作品追加依頼
OP/ED追加依頼
OP/ED not found
作品提案承認欄

格言 fromスクライド
この世の理は即ち速さ
20年かければ馬鹿でも
傑作小説を書ける

助けられたら助け返す
それが俺のルール

強くなるには
一番弱い考えをする事だ
そしてその考えに反逆する




右側に何か入れてみるテスト


仕事でのサイト
介護DB
Helpyou
Doctor career
Nurse career
上へ ↑上へ 最速検索作品DB皆声