Oracle BerkeleyDBのインストール/設定/利用方法
Oracle BerkeleyDBとは何か
Oracle BerkeleyDBとは、アプリケーションに組み込んで使うKV型データベースです。
近頃は機能が拡張され、SQLを扱う事も出来ます。
データベースとしては、Hash又はBTREE又はQueue又はReconoの何れかの構造に基づくKey Value型DBです。
データベースへのアクセスはKey Value型アクセスの他、SQLやXMLでのアクセスもサポートされるようになりました。
ネットワーク通信が必要ない、同じアプリケーション空間内で作動するDBなので、当然速度的には最高レベルのパフォーマンスを発揮します。
また、結果として、組み込み系ソフトのDBとしての利用にも向いています。
但し、アプリケーションと同じメモリ空間で動く為、同時アクセスの時の制御等、簡単そうで扱いが難しい面もあるので、同時書込の制御等には注意が必要です。
ここではそのBerleyDBのインストール方法についてまとめます。
BerkeleyDBのライセンス形態
https://www.oracle.com/technetwork/database/berkeleydb/downloads/licensing-098979.html
がソース元になりますが、2つのライセンス形態があります
オープンソースモデル: オープンソースへの利用または非配布型ソフトでの利用を許可
コマーシャルモデル: アプリケーションのコードを公開する必要がなく、配布するソフトウェアに組み込む事もできる。
Oracle BerkeleyDBのインストール
古いバージョンで良ければ、BerkeleyDBは大概どんなデータベースにも入っています。
/usr/lib64/libdb.so
又は
/usr/lib/libdb.so
がそれにあたります。
但し、Oracleが買収してから、手入れしているOracle DBの最新版は入っていないので、最新版を入れたい場合には、ソースコードから自分でビルドしてインストールする必要があります。
ソースコードのダウンロード
https://www.oracle.com/database/technologies/related/berkeleydb-downloads.html
にアクセスしして、ソースコードをダウンロード。
基本的に最新バージョンをダウンロードすれば良いかと思います。
なお、ダウンロードするには、Oracleのサイトの会員になる必要がありますが、無料でアカウントの取得は可能です。
環境変数の用意
まず
echo $LD_LIBRARY_PATH;
と打って、/usr/local/libが先の方に出てこないのなら
vi ~/.bash_profile;
を打って
LD_LIBRARY_PATH=/usr/local/lib64:/usr/local/lib:/usr/lib64:/usr/lib:/lib64:/lib export LD_LIBRARY_PATH
という行を追加して、
. ~/.bash_profile;
と打って反映。
再度
echo $LD_LIBRARY_PATH;
と打って環境変数への反映を確認
BerkeleyDBのビルドとインストール
ソースコードを解凍したディレクトリにcdしてから
cd build_unix; make clean; ../dist/configure --prefix=/usr/local --enable-cxx --enable-sql; make; make install;
システム全体で共有ライブラリとして使えるように
echo /usr/local/lib > /etc/ld.so.conf.d/usr-local-lib.conf
こうするとapache2.4とかでもBerkeleyDBを見た時にインストールされた版の方を見るようになる
各言語での実装
C言語での使い方
#include <sys/types.h> #include <stdio.h> #include <db.h> #define DATABASE "access.db" int main(){ DB *dbp; DBT key, data; int ret, t_ret; if((ret = db_create(&dbp, NULL, 0)) != 0){ fprintf(stderr, "Db_create: %s\n", db_strerror(ret)); exit(1); } if((ret=dbp->open( dbp, NULL, DATABASE, NULL, DB_BTREE, DB_CREATE, 0644))!=0){ dbp->err(dbp, ret, "%s", DATABASE); goto err; } memset(&key, 0, sizeof(key)); memset(&data, 0, sizeof(data)); key.data="fruit"; key.size=sizeof("fruit"); data.data="apple"; data.size=sizeof("apple"); if((ret=dbp->put(dbp, NULL, &key, &data, 0))==0){ printf("db: %s: key stored.\n", (char *)key.data); } else{ dbp->err(dbp, ret, "DB->put"); goto err; } if((ret=dbp->get(dbp, NULL, &key, &data, 0))==0){ printf("db: %s: key retrieved: data was %s.\n", (char *)key.data,(char *)data.data); } else{ dbp->err(dbp, ret, "DB->get"); goto err; } if((ret=dbp->del(dbp, NULL, &key, 0))==0){ printf("db: %s: key was deleted.\n", (char *)key.data); } else{ dbp->err(dbp, ret, "DB->del"); goto err; } err: if((t_ret = dbp->close(dbp, 0))!=0 && ret==0){ ret=t_ret; } exit(ret); }
Perlからの使い方
ダウンロードしたソースコードのディレクトリで
cd lang/perl/BerkeleyDB;
vi config.in
し
INCLUDE = /usr/local/include LIB = /usr/local/lib
が効くようにコメントアウト。
/usr/lib64/libdb.so
があったら
mv /usr/lib64/libdb.so /usr/lib64/libdb.so.nouse
で移動
そしたら
perl Makefile.PL; make test; make; make install;
Perlでのプログラム例
BDB::Wrapperを追加インターフェースとして使用
Tokyo::Cabinetを使ったらの部分も混ざっていますが、そこはコメントアウトされています。
#!/usr/local/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'}){ =pod $self->{'tch'}=TokyoCabinet::BDB->new(); tchdbtune(hdb, rnum * 3, 0, 0, 0); $self->{'tch'}->tchdbsetxmsiz($cache) if $self->{'cache'}; =cut } else{ system('rm -rf /dev/shm/bdb_home'.$pwd.'/speed'); system('rm -rf /www/ichiji/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'}){ =pod 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)); } =cut } 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'}){ =pod 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)); } =cut } 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'}){ =pod 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)); } =cut } 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; }
SQLでアクセスしてみる
dbsql test.bdb
と打って、test.bdbというDBファイルを生成しながらSQLコンソールを開く。
そして
create table t(a integer, b string); insert into t values (1, 'one'); insert into t values (2, 'two'); select * from t;
と一通りSQLが使える事を確認する。
その他言語からの使い方
https://docs.oracle.com/database/bdb181/html/index.html
をご参照下さい。
C、C++、C#、Java、TCL向けの文章が提供されています。