phpでの同時読み書きファイル競合の解決策(ファイルロック適用例)

5941 ワード

PHP(外文名:Hypertext Preprocessor、中国語名:「ハイパーテキストプリプロセッサ」)は、一般的なオープンソーススクリプト言語です.文法はC言語、JavaとPerlの特徴を吸収し、入門の敷居が低く、学習しやすく、広く使用され、主にWeb開発分野に適している.PHPのファイル接尾辞の名前はphpです.
本稿ではphpにおける同時読み書きファイルの衝突の解決策(ファイルロック応用例)について説明し、興味のある学生は参考の下で説明する.
ここでは4つの高同時読み書きファイルのスキームを提供し,それぞれ利点があり,php同時読み書きファイルの競合の問題を自分の状況に応じて解決できる.
日本のIPが高くない、あるいはコンカレント数があまり大きくない応用については、一般的にはこれらを考慮する必要はありません!一般的なファイル操作方法では全く問題ありません.しかし、同時高であれば、ファイルの読み書き操作を行う場合、複数のプロセスが1つのファイルを操作する可能性があります.この場合、ファイルへのアクセスを独占しないと、データが失われやすくなります.例えば、オンラインチャットルーム(ここではチャット内容をファイルに書き込むと仮定する)では、同じ時刻にユーザAとユーザBがデータ保存ファイルを操作し、まずAがファイルを開いてから中のデータを更新するが、ここBもちょうど同じファイルを開いて、中のデータを更新する準備をしている.Aが書いたファイルを保存すると、ここでは実はBがファイルを開いています.しかし、Bが再びファイルを保存すると、ここではすでにデータの損失が発生している.ここでBユーザーが開いているファイルが変更されたとき、Aユーザーもこのファイルを変更したことを全く知らないため、最後にBユーザーが変更を保存すると、ユーザーAの更新が失われる.このような問題に対して、一般的な解決策では、1つのプロセスがファイルを操作する場合、まず他のプロセスをロックすることは、ここではそのプロセスだけがファイルを読み取る権利があることを意味し、他のプロセスが今読んでいる場合、全く問題はありませんが、この場合、プロセスが更新しようとすると、操作が拒否されます.以前にファイルをロックするプロセスで、ファイルの更新操作が完了すると、排他的なIDが解放され、ファイルは変更可能な状態に戻ります.次に同じように、そのプロセスがファイルを操作するときにファイルにロックがかかっていない場合、安心して大胆にファイルをロックして、一人で楽しむことができます.一般的なシナリオは次のとおりです.
 
  
$fp=fopen('/tmp/lock.txt','w+');
if (flock($fp,LOCK_EX)){
    fwrite($fp,"Write something heren");
    flock($fp,LOCK_UN);
}else{
    echo 'Couldn't lock the file !';
}
fclose($fp);

しかし、PHPではflockはそんなによく働いていないようです.多くの同時性の場合、リソースを独占したり、即時に解放したり、まったく解放したりせず、デッドロックを起こしたりして、サーバのcpuの占有量が高くなり、サーバを完全に死なせることもあるようです.多くのlinux/unixシステムでは、このような状況が発生しているようです.だからflockを使う前に、慎重に考えなければなりません.では、解決策はありませんか?実はそうではありません.flock()を適切に使用すれば、デッドロックの問題を完全に解決することができます.もちろんflock()関数の使用を考慮しなければ、私たちの問題を解決するためにも良い解決策があります.私個人の収集と総括を経て、大体解決策をまとめたのは以下のいくつかあります.シナリオ1:ファイルをロックする場合、タイムアウト時間を設定します.以下のように大まかに実現される.
 
  
if($fp=fopen($fileName,'a')){
 $startTime=microtime();
 do{
  $canWrite=flock($fp,LOCK_EX);
  if(!$canWrite){
   usleep(round(rand(0,100)*1000));
  }
 }while((!$canWrite)&&((microtime()-$startTime)<1000));
 if($canWrite){
  fwrite($fp,$dataToSave);
 }
 fclose($fp);
}

タイムアウトは1 msに設定されており,ここで時間内にロックが得られなければ繰り返し取得し,ファイルに対する操作権まで直接取得するのは当然である.タイムアウト制限が到来した場合は、すぐに終了し、ロックを他のプロセスに操作させる必要があります.
シナリオ2:flock関数を使用せずに、一時ファイルを借りて読み書きの競合の問題を解決します.大まかな原理は以下の通りです:(1)更新する必要があるファイルを私たちの一時ファイルディレクトリに考慮し、ファイルの最後の修正時間を変数に保存し、この一時ファイルにランダムで、重複しにくいファイル名を取ります.(2)このテンポラリファイルを更新した後、元のファイルの最終更新時間と以前に保存した時間とが一致しているか否かを検出する.(3)最後の変更時間が一致した場合は、変更したテンポラリファイルの名前を元のファイルに変更し、ファイル状態が同期して更新されるようにファイル状態をクリアする必要があります.(4)ただし、最後の変更時間が以前に保存したものと一致する場合、この期間中に元のファイルが変更されたことを示す.この場合、一時ファイルを削除してfalseに戻る必要があり、ファイルが他のプロセスで操作されていることを示す.実装コードは次のとおりです.
 
  
$dir_fileopen='tmp';
function randomid(){
    return time().substr(md5(microtime()),0,rand(5,12));
}
function cfopen($filename,$mode){
    global $dir_fileopen;
    clearstatcache();
    do{
  $id=md5(randomid(rand(),TRUE));
        $tempfilename=$dir_fileopen.'/'.$id.md5($filename);
    } while(file_exists($tempfilename));
    if(file_exists($filename)){
        $newfile=false;
        copy($filename,$tempfilename);
    }else{
        $newfile=true;
    }
    $fp=fopen($tempfilename,$mode);
    return $fp?array($fp,$filename,$id,@filemtime($filename)):false;
}
function cfwrite($fp,$string){
 return fwrite($fp[0],$string);
}
function cfclose($fp,$debug='off'){
    global $dir_fileopen;
    $success=fclose($fp[0]);
    clearstatcache();
    $tempfilename=$dir_fileopen.'/'.$fp[2].md5($fp[1]);
    if((@filemtime($fp[1])==$fp[3])||($fp[4]==true&&!file_exists($fp[1]))||$fp[5]==true){
        rename($tempfilename,$fp[1]);
    }else{
        unlink($tempfilename);
  // ,
        $success=false;
    }
    return $success;
}
$fp=cfopen('lock.txt','a+');
cfwrite($fp,"welcome to beijing.n");
fclose($fp,'on');

上記のコードで使用する関数については、(1)rename();ファイルまたはディレクトリの名前を変更します.この関数はlinuxのmvに似ています.ファイルやディレクトリのパスや名前を更新するのは便利です.しかし、Windowsで上のコードをテストすると、新しいファイル名がすでに存在する場合は、現在のファイルが存在しているというnoticeが与えられます.しかしlinuxではよく働いています.(2)clearstatcache();ファイルのステータス.phpをクリアすると、すべてのファイル属性情報がキャッシュされ、より高いパフォーマンスが得られますが、マルチプロセスがファイルの削除や更新操作を行う場合、phpがキャッシュ内のファイル属性を更新する時間がなく、最終更新時間まで実際のデータにアクセスできない場合があります.この関数を使用して、保存したキャッシュをクリアする必要があります.
シナリオ3:操作されたファイルをランダムに読み書きし、同時実行の可能性を低減する.このスキームは,ユーザアクセスログを記録する際に採用されることが多いようである.以前はランダム空間を定義する必要があり、空間が大きいほど同時の可能性は小さくなり、ここではランダム読み書き空間が[1-500]であると仮定すると、私たちのログファイルの分布はlog 1~log 500と等しくない.ユーザがアクセスするたびに、log 1~log 500間のいずれかのファイルにデータをランダムに書き込む.同じ時刻に2つのプロセスがログを記録し、Aプロセスは更新されたlog 32ファイルである可能性がありますが、Bプロセスは?このとき更新される可能性はlog 399である.Bプロセスにもlog 32を操作させる場合、確率は基本的に1/500であり、差は多くなくゼロに等しいことを知っておく必要がある.アクセスログを分析する必要がある場合は、これらのログを統合してから分析するだけです.このスキームを使用してログの利点を記録する場合、プロセス操作がキューに並ぶ可能性は小さく、プロセスが迅速に各操作を完了することができます.
シナリオ4:操作するすべてのプロセスを1つのキューに入れます.その後、専用のサービスを置いてファイル操作を完了します.キュー内の各除外プロセスは最初の具体的な操作に相当するので、最初のサービスはキューから具体的な操作事項に相当するものを取得するだけでいいです.もしここに大量のファイル操作プロセスがあれば、大丈夫です.私たちのキューの後ろに並べばいいです.並べたい限り、キューの長さは関係ありません.
以前のいくつかの案には、それぞれメリットがあります!大きく2つにまとめることができる:(1)キューが必要(影響が遅い)例えば、シナリオ1、2、4(2)キューが必要でない.(影響が速い)シナリオ3キャッシュシステムを設計する際、一般的にシナリオ3は採用されません.シナリオ3の分析プログラムと書き込みプログラムは同期していないので、書く時間に、その時の分析の難しさを全く考えず、書く行だけにしています.キャッシュを更新するときにランダムファイルの読み方も採用すると、キャッシュを読むときに多くのプロセスが増加するようです.しかし,シナリオ1,2は全く異なり,書く時間は待つ必要がある(ロックの取得に成功しない場合は繰り返し取得する)が,ファイルを読むのは便利である.キャッシュを追加する目的は、データの読み取りボトルネックを低減し、システムのパフォーマンスを向上させることです.上から個人の経験といくつかの資料の総括のために、何か間違っているところがあって、あるいは話していないところがあって、各位の同業者の指摘を歓迎します.