さじろーどっとねっと
<技術文書>

ADD方式
Perl DATABASEでは、データ管理にADD方式を採用しています。この章ではADD方式の概要を説明します。

ADD方式では以下のようにファイルに設定します。ファイル名は「perldbdat?.cgi」となります。

$perldb->insert("test","aaa<>bbb<>ccc<>ddd<>");を実行した場合・・・

perldbdattest.cgi
???????????????????
000000000000002
000000000000002<>aaa<>bbb<>ccc<>ddd<>

???????????????????

読み込む時には、連想配列に入れます。

???????????????????
open(IN,"dbdata/perldbdattest.cgi");
%data=;
close(IN);
???????????????????

この方式により、インデックス部分(この例では「000000000000002」)に対する処理が、全部のデータを検索するよりも高速になります。データの検索頻度を考えるとインデックス部分のソートがかなりのパフォーマンスネックになります。そのためこのような処理になりました。

???????????????????
@sortkeys = sort keys(%data);
foreach(@sortkeys){
print "$data{$_}"; # ソートされた順番に表示されます。
}

# 逆順にソートしたい場合は、リバースします
@sortkeys = reverse sort keys(%data);
foreach(@sortkeys){
print "$data{$_}"; # ソートされた順番に表示されます。
}
???????????????????

データを削除した場合には以下のようになります。

$perldb->delete("test","1 eq aaa");を実行した場合・・・

perldbdattest.cgi
???????????????????
000000000000002
000000000000002<>aaa<>bbb<>ccc<>ddd<>
000000000000002
?この行は空白になります

???????????????????

ファイルを昇順に読み込みますので、先に読み込まれたデータ「000000000000002<>aaa<> bbb<>ccc<>ddd<>」が$data{"000000000000002"}に設定されます。その後、「空白」のデータが再度「$data{"000000000000002"}」に設定されますので結果として、「$data {"000000000000002"}」は「""」になります。

???????????????????
open(IN,"dbdata/perldbdattest.cgi");
%data=;
close(IN);

print "$data{"000000000000002"}"; # なにも表示されません
???????????????????

処理内容ではマスターファイルと更新ファイルに分けられており、事前にマスターデータを読み込み、次に更新データを読み込みます。

マスターファイル
???????????????????
000000000000002
000000000000002<>aaa2<>bbb2<>ccc2<>ddd2<>
000000000000003
000000000000003<>aaa3<>bbb<>ccc<>ddd<>
000000000000004
000000000000004<>aaa4<>bbb4<>ccc4<>ddd4<>

???????????????????

トランザクション
???????????????????
000000000000002 # 削除したレコード
?この行は空白にです
000000000000003 # 更新したレコード
000000000000003<>aaa3<>bbb3<>ccc3<>ddd3<>

???????????????????

Selectした場合、以下の結果が得られます。
???????????????????
000000000000002<>aaa2<>bbb2<>ccc2<>ddd2<>
000000000000003<>aaa3<>bbb3<>ccc3<>ddd3<>
000000000000004<>aaa4<>bbb4<>ccc4<>ddd4<>
???????????????????

TOP?LOCK方式
Perl DATABASEでは、ファイルロックにTOP?LOCK方式を採用しています。この章ではTOP?LOCK方式の概要を説明します。

ファイルのロックの方法は、flockを使用するケースが多くありますが、flockを使用する場合、特定のライブラリを必要とします。PERLDBではOPEN環境での稼動を目的としておりますので、特定ライブラリの仕様は規定外になります。

その問題を解決し、どの環境でも動作する、確実いロックする方法でTOP?LOCK方式を採用しました。以下に概要を説明します。

TOP?LOCK方式では特定ファイルへロック要求を出します。そして、そのファイルを読み込んだ時に自分の書き込みが一番上(先頭)にあれば使用する権利を得たことになります。

順を追って説明しましょう。

1.ロックの要求を出します。
open(LOCK,">>test.lock");
print LOCK "myname\n";
close(LOCK);

2.ロックできたか見ます。
open(LOCK," @lock=;
close(LOCK);
if($lock[0] eq "myname"){
print "ロック成功!";
}else{
print "ロック失敗!";
}

これでロックの正否が解ります。しかし、これでは同一実行ファイルがロックをかけてしまった場合に「myname」がだれなのかわかりません。自分自身を認識する必要があります。そこでランダムな数字を使います。(Sajiro DATABASEでは乱数値+プロセスIDを使用しています)

open(LOCK,">>test.lock");
$id=rand(100);
print LOCK "$id\n";
close(LOCK);

open(LOCK," @lock=;
close(LOCK);
if($lock[0] eq "$id"){
print "ロック成功!";
}else{
print "ロック失敗!";
}

しかし、WEBアプリケーションの場合、ロックをかけたプロセスが異常終了する場合があります。その時はいつまで経っても自分のIDは先頭にくることはなく結果として他プロセスがロックした状態になります。それを防ぐために時間も必要になります。

open(LOCK,">>test.lock");
$id=rand(100);
$tm=time;
print LOCK "$id<>$tm<>\n";
close(LOCK);

ロックした時間を見て古いデータであれば削除しても良いとします。

open(LOCK," @lock=;
close(LOCK);
($wid,$wtm)=split(/<>/,$lock[0]);
if($wid eq "$id"){
print "ロック成功!";
}elsif($wtm < time - 600){
print "ロックファイル削除!";
unlink("test.lock");
}else{
print "ロック失敗!";
}

しかし、削除してしまうとほかの処理が実はロックしている場合があります。その時にロックが重複してしまいます。それを防ぐために再度、データ反映時にロックの確認をします。

# ファイルのロック
open(LOCK,">>test.lock");
$id=rand(100);
$tm=time;
print LOCK "$id<>$tm<>";
close(LOCK);

# ファイルのロック確認
open(LOCK," @lock=;
close(LOCK);
($wid,$wtm)=split(/<>/,$lock[0]);
if($wid eq "$id"){
print "ロック成功!";
}elsif($wtm < time - 600){
print "ロックファイル削除!";
unlink("test.lock");
exit;
}else{
print "ロック失敗!";
exit;
}

# ファイルの更新
open(DATA,"workdata.tmp");
print DATA "01<>更新データを追加します<>");
close(DATA);

# 再度ファイルのロック確認
open(LOCK," @lock=;
close(LOCK);
($wid,$wtm)=split(/<>/,$lock[0]);
if($wid eq "$id"){
print "ロック成功!";
}elsif($wtm < time - 600){
print "ロックファイル削除!";
unlink("test.lock");
exit;
}else{
print "ロック失敗!";
exit;
}

# ロック成功のときだけ処理を本番ファイルにします。
rename("workdata.tmp","workdata.dat");

全てのロック情報は、ロック要求から始まり、ロック確認、データの書き込み、ロック確認、本番ファイルへの反映となります。

実際には、IDが0?100では同じ番号が出る場合がありますので、30桁以上の数値を使用しています。プロセスIDを使用するとWinでうまく行かないので乱数を使っています。


ブックマークに追加する