トップへ > Web Program Lab.> No.4 ファイルのロック
ウェブ・プログラムの研究所 Web Application Programming Lab.
一つ前にもどる 目次にもどる 次のページへ
●ファイルのロック(排他処理)の必要性

前回のカウンターを作った最後に、「このプログラムには重大な欠陥がある」ということを書いたと思います。この欠陥とは、カウンターの値を保存しているファイル(以下、データファイルと呼びます。)にあります。このデータファイルは、カウンタープログラムが起動されるとき、つまり、誰かがそのページにアクセスしたときに読み出されたり、書き込まれたりしますので、不特定多数の人に同時にアクセスされる可能性があるのです。

これがどのような問題を引き起こすのでしょうか?

通常、2人以上でファイルを利用することを「共有」と言います。ファイルの中身を見るだけ、いわゆる「参照 」するだけなら、何も問題はありません。が、このファイルの中身を皆で書き換えたりするときに大きな問題が 発生します。

通常一人で読み書きする場合はどのようにしているのでしょうか?


読むだけの場合
  1.ファイルを開く
  2.データを取り出す
  3.ファイルを閉じる
  4.見る



書く場合
 1.ファイルを開く
 2.データを取り出す
 3.(ファイルを閉じる)
 4.取り出したデータに付け加える(もしくは、加工する)
 5.(ファイルを開く)
 6.データを書き込む
 7.ファイルを閉じる


以上のようになっています。書く場合には、もしかすると、3、5の手順はないこともあるでしょう。普通に考 えるとなにも問題はないのですが、この操作を複数で同時に行った場合を考えてみてください。

上の例の場合、A、Bと2人が「ほぼ」同時に同じページを訪問したと想定します。

ファイル書き込みの流れ Aさんの方が若干早かったので、Aがカウンターファイルを開きました。すると、カウンターには丁度「100」 という数字があったので、そこに自分自身のアクセスを足して101というファイルを書き込もうとしています 。Aがファイルを書き込む直前にBがアクセスしてきました。Bはカウンターファイルを開きました。Aの101と いう値はまだ書き込まれていないので、ファイルは「100」のままです。

その後、Aが101のファイルを書き込みました。Bは開いたファイルの数字が100のままなので、それに自分 自身のアクセス数を足して101というファイルで書き込みました。

最終的にA、B2人のアクセスがあったにもかかわらず、カウンターは100から101と1件しかカウントされま せんでした。

通常このような現象がおきるのは、すごい確立だとは思うのですが、アクセス数がそこそこ増えてくると、このよ うなことは起こりえます。また、今の例はカウンターでしたが、掲示板だったとすれば、せっかく書いてくれた 掲示内容が反映されない、ということになります。また、Eコマースなどの注文などのデータの場合には、金銭 トラブルになりかねません。

このようなことがおきないように、ファイルを複数で同時に使う場合、つまり共有する場合には「排他」という 処理を行います。ファイルを「ロックする」、とも言います。

【 Word解説!】
「排他(ファイルロック)とは、、、
ネットワークで情報を共有するようなシステムになってくると、一つのファイルを大勢で読み書きすることが多 くなってきます。このような場合、自分がファイルに書き込みを行っているときに、他の誰かも同じように書き 込みを行っている場合、書き込まれたファイルに矛盾が生じてしまうことがあります。この矛盾を防ぐために、 自分が書き込んでいる間は、他の誰にも書き込みをさせない処理のことを「排他」といいます。」



●排他のプログラム

共有するデータをMySQLといったようなDBMSで操作するときは、これらソフトが排他制御を行ってくれるので、割と簡単に排他の処理ができるらしいのですが、プロバイダーや無料のレンタルスペースだと、データベースソフトは使えないので、通常のテキストファイルをデータファイルとして、自分で排他制御する必要があります。(DMBSとの連携はまだ勉強中です・・)

理屈は簡単です。自分が書き込みをしようと思ってファイルを開くときには、他の誰にもファイルを開かせない、それだけのことです。流れを書くと。


1.ファイルをロックして排他制御する。(これで、誰もファイルを開くことができない)
2.ファイルを開いてデータを参照
3.必要な処理を行う
4.ファイルに処理後のデータを保存
5.ファイルのロックを解除する。(誰でもアクセス可能な状態にする。)


以上です。

これは、書き込みを行う場合だけに発生する問題ですので、読み込みしかしないデータには排他の処理は無意味です。

そこで、サンプルとして使ってきたカウンターのプログラム(Perl)ではどのような手法で排他を行うかを考えてみたいと思います。

Perlには「flock」というファイルの排他制御を行う関数があります。ただ、これはサーバーのOSに依存するので、使えない場合があります。と書かれていることが多いのですが、自分の経験上、使えないというサーバーを使ったことがありません。WindowsNT系でも使えたので、おそらく大丈夫だと思います。(Windows9X系は使えません。!)

いろんなCGI用Perlスクリプトを配布しているサイトでは、Symlinkなどを利用してファイルの排他(ロック)を行っているようですが、Symlinkなどは、もともとファイルの排他をするためのものではなく、アルゴリズム的にも不完全だという指摘も聞きます。個人的には、不完全な排他なら、しないほうがいいのでは・・・とも思ってます。

flockを利用してファイルに排他をかけることを考えると、以下のようなプログラムになりそうです。前回行ったカウンターのプログラムに排他をかける例で示してみます。


01: #!/usr/local/bin/perl
02:
03: # ロック用ファイルを作成
04: open(LOCK, ">lock.temp");
05: flock(LOCK,2); # ロック用ファイルをロックする
06:
07: # count.datというファイルから現在のカウント数を読み込む
08: open (IN, "count.dat");
09: $count=<IN>;
10: close(IN);
11:
12: # 現在のカウント数$countに1を追加
13: $count=$count+1;
14:
15: # 1増加したカウントを別の一次ファイルに保存
16: open (OUT, ">count.temp");
17: print OUT $count;
18: close(OUT);
19:
20: # 別の一次ファイルから本来のファイルに名前を変更
21: rename("count.temp","count.dat");
22:
23: # 最初に作ったロック用ファイルを削除
24: unlink("lock.temp");
25:
26: # ロックしたファイルを解除
27: flock(LOCK,8);
28: close(LOCK);
30:
31: # ここからHTMLでの表示
32: print "Content-Type: text/html\n\n";
33: print "<html><head><title>test</title></head>";
34: print "<body>";
35: print "あなたは $count 番目の訪問者です。";
36: print "</body></html>";
37:
38: __END__

*左端の番号は便宜的に付けた行番号です。


どうでしょうか?これで排他がかかりました。

それでは、一部簡単にですが、解説したいと思います。
4〜5行目で、以前はなかったロックのための処理を行います。ここで、ちょっと「おや?」と思うのは、今カウンターとして変更したいファイルは「count.dat」なのに、どうしてわざわざ別のファイル「lock.temp」を作って、それをロックするのか、ということです。

03: # ロック用ファイルを作成
04: open(LOCK, ">lock.temp");
05: flock(LOCK,2); # ロック用ファイルをロックする


上記のように別のファイルを利用するのは、変更をしたいファイルを直接ロックすることも可能なのですが、こうすると、ファイルを開いてロックをするまでの間に若干の時間的隙間ができてしまうので、その隙間の間にファイルを開くことが出来てしまうからです。

実際、自分のローカルマシンで短時間に直接ファイルをロックする方法で1000回書き込みを行うプログラムを書いて、2つ同時に実行させてみました。その結果、本来なら1000回のアクセスが2つ同時に実行されているので、カウンターは2000になるべきなのですが、全く違う数字になっていました。

その後、上記のように別にロックファイルを作ってそちらをロックしてから、ファイルの書き込みを行う処理に変更して同じ実験をしたのですが、こちらは、2つのプログラムの処理終了後はカウンターはちゃんと2000となっていました。

(この実験については、別の機会にここで公開します。ファイルロックは奥が深いことを思い知らされました・・・)

ということで、実験のように数秒間で1000以上のアクセスがあることなどは、個人のページではまずないと思うのですが、別にロックファイルを作った方が正しく処理ができるということが理解できます。

それ以降は、前回書いたカウンタープログラムと同じ手順ですが、ここで、更に一点違う場所があります。 普通なら、1増加させたデータを直接カウンター用のデータファイル「count.dat」に書き込むべきなのですが、これをあえて別の仮ファイル「count.temp」に書き出しています。(16行をご覧ください。)

15: # 1増加したカウントを別の一次ファイルに保存
16: open (OUT, ">count.temp");
17: print OUT $count;
18: close(OUT);
19:
20: # 別の一次ファイルから本来のファイルに名前を変更
21: rename("count.temp","count.dat");


別のファイルに書き出した後に、21行目で名前を変更させて、元のファイルにしています。こうする理由ですが、ファイルを書き込んでいる途中でシステムが強制終了等になると、ファイルが壊れてしまうため、それを防ぐ目的です。(この部分のアイディアは、ここのレンタルサーバーの技術情報からお借りしました。m(__)m)

最後に、ロックファイル用に作成した仮ファイルを削除して、ロックを解除して処理終了です。

23: # 最初に作ったロック用ファイルを削除
24: unlink("lock.temp");
25:
26: # ロックしたファイルを解除
27: flock(LOCK,8);
28: close(LOCK);


ただ、17行目のロックの解除は「 close() 」がある場合は不要です。念のために書いています。



●本当にロック(排他処理)は必要?

最後に、ファイルのロックについていろいろと書きましたが、本当に排他の処理が必要か?という問いに関しては、少々疑問もあります。個人で作っているページのレベルなら排他処理をしなくても十分に耐えられるのかな・・・と思ってます。逆に、排他処理を行っていたにもかかわらず壊れてしまった、という経験もあります。

自分の経験を少しだけかくと・・・

アンケート集計のプログラムを書いたことがあります。20人程度が5分ほどの間にWeb上でアンケートに答えて、その集計を行うというものです。これを90日程度毎日繰り返したことがありましたが、排他処理なしで全く問題ありませんでした。たまたまかもしれませんが、これくらいの頻度で壊れることのないレベルです。日に50〜100人程度のアクセスがある個人ページの掲示板くらいなら、排他処理はしなくてもOKなのかなぁ・・・とも思いますね。

正直、排他をかけた場合とかけない場合で、プログラム的にどの程度の負荷になるかはよくわかってませんが、下手な排他処理をかけるくらいなら、かけない方が壊れにくいのかなぁ、、、とも思いますね。ということで、前回「欠陥があります!」と書いておきながら矛盾しますが、ロックは時と場合に応じて・・・ということでしょうか?

商用サイトや、信頼性をかなり追求されるようなプログラムには必要でしょうが・・・というか、そういうサイトを作る場合は、そもそもDBMSなどのプログラムと連携させるべきでしょう。

ファイルロックは奥が深すぎて、最初に勉強・・・とおもって手がけるには大きすぎる課題でした。実験なども色々してみましたが、まとまりきっていないので、もう少しまとめてから公開したいと思います。m(__)m


(2003/9/19 by あいまい)
一つ前にもどる 目次にもどる 次のページへ

トップへ > Web Program Lab.> No.4 ファイルのロック

copyright(C) 2001-2003 all rights reserved
『ウェブ・プログラムの実験場!』

by あいまいモード・コム(www.iMYmode.com)