1. 2008/05/07 「Berkeley DB > 同時アクセス > 書き込みの排他処理(CDB)の注意事項」 [この書込みのみ表示(記事URL紹介用) / 書込変更/削除 / トラックバック送信 / 共有分類に追加(タグ付け)](この日の語りにコメント/感想つけてみる)1. BerkeleyDB側にロック処理を任せるには 2. __db.001というようなファイルは何? 3. BerkeleyDBに関する日本における情報 4. Windows上で同時書き込み実験 5. BDBファイルの破損のチェック 6. BDBファイルの回復
1. BerkeleyDB側にロック処理を任せるには
DB_INIT_CDB | DB_CREATE | DB_INIT_MPOOL の3つのFlagをEnvに対して設定して開くことが必要。 CDBという仕組みでロックの管理をしてくれる。
2. __db.001というようなファイルは何?
DB_CREATEをEnvのFlag内で指定していると、__db.001, __db.002, __db.003というファイルができるが、プログラムが途中で死ぬとロック状態を残してしまうので、後で自分で消さなければならない。 これがどこに出来るかは、-Homeで指定した場所による。 ただ、絶対パスでBDBの場所を書いていないと、プログラムを実行したcurrent directoryに出来てしまうので注意。 db_stat -N -Co をそのディレクトリで打つとロックの状況を見れる。 またBDBファイル毎に異なるようにHomeを用意しなければならない(そうでないと__db.001などが共有されてロックまで他と共有してしまう)。 HomeはBDBファイルを開く前にディレクトリとして作っておく(そうでないと無効)。
なお、4.4らへん(?)からロック状態で死んでしまったものを回復するためのAPIをBerkeleyDB側でも提供しはじめたようだが、今のところBerkeleyDB.pmはその為に必要なenv_failchkを提供していない。 なのでそこに対応する処理は自分で作る(正確には機能が提供されていても色々と作ることが必要そう/まあ基本的にC API経由で使うものなので)。 自分はタイムアウト処理を活用してタイムアウトしたらそのファイル群を消去してしまっているが、 その実装においてはalarmが今のところ少なくともcygwinのperlではサポートされていないことに注意。
3. BerkeleyDBに関する日本における情報
< 日本にはBerkeleyDBに関する突っ込んだ情報はネット上には皆無。 これだけ年月が経ちながら不思議な位情報無しなので英語が出来ないと厳しいだろうなぁ。 ちなみに英語圏だとBerkeleyDBの本も出ていたりします(自分は手元にあります)。
4. Windows上で同時書き込み実験
同時書き込みを100プロセスで行う実験の結果: 1. 壊れませんでした。 2. abortにはロックを残してしまうという弱さはあり(ここは自分でフォローしないといけない)。 3. ファイルの破損は起きませんでしたが、putに失敗することは何度かありました。書き込みが失敗することがあるということは前提でエラーハンドル処理を実装しておきましょう。
5. BDBファイルの破損のチェック
db_verify BDBファイル と打つと確認できます。 なお、書きこみ中だと正常なファイルでもErrorを返してくることがあるので、常時書き込まれる系のBDBは何度かやってみて本当にエラーなのか確認してみましょう。
6. BDBファイルの回復
BDBファイルが破損してしまった場合 1. トランザクション処理をしていればログディレクトリから回復させることができる。 2. トランザクション処理をしていない場合は、バックアップファイルからその時点まで復旧させるか、読めるところを読み込んでファイルを再生成する。 ログファイル含めてコストがかかりますが、確実な処理を要するのなら1.で。
ちなみに自分はこんなプログラムを作って2のケースの場合リカバリーしています。
#!/usr/local/bin/perl -w use strict; use BerkeleyDB; use BDB::Wrapper; use CGI::Accessup; use File::Copy; my $c=new CGI::Accessup; my $bdbw=new BDB::Wrapper; my $bdb=shift || die "no bdb";
my %checked=(); my $key_cnt=0; File::Copy::copy($bdb, $c->shmf($bdb)); if(my $dbh=$bdbw->create_read_dbh($c->shmf($bdb))){ if(my $wdbh=$bdbw->create_write_dbh($bdb.'.wtmp')){ my $key; my $value; if(my $cursor=$dbh->db_cursor()){ while($cursor->c_get($key, $value, DB_NEXT)==0){ if($checked{$key}){ print $key." appeared again\n"; last; } $wdbh->db_put($key, $value); $key_cnt++; $checked{$key}=1; } $cursor->c_close(); } $wdbh->db_close(); } $dbh->db_close(); } unlink ($c->shmf($bdb)); $c->lockf($bdb); $c->rename($bdb.'.wtmp', $bdb); $c->unlockf($bdb);
print $key_cnt."\n"; |
|