欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

php中并發(fā)讀寫文件沖突的解決方案

 更新時間:2013年10月25日 09:27:21   作者:  
在這里提供4種高并發(fā)讀寫文件的方案,各有優(yōu)點,可以根據(jù)自己的情況解決php并發(fā)讀寫文件沖突的問題。

對于日IP不高或者說并發(fā)數(shù)不是很大的應(yīng)用,一般不用考慮這些!用一般的文件操作方法完全沒有問題。但如果并發(fā)高,在我們對文件進行讀寫操作時,很有可能多個進程對進一文件進行操作,如果這時不對文件的訪問進行相應(yīng)的獨占,就容易造成數(shù)據(jù)丟失。
例如:一個在線聊天室(這里假定把聊天內(nèi)容寫入文件),在同一時刻,用戶A和用戶B都要操作數(shù)據(jù)保存文件,首先是A打開了文件,然后更新里面的數(shù)據(jù),但這里B也正好也打開了同一個文件,也準(zhǔn)備更新里面的數(shù)據(jù)。當(dāng)A把寫好的文件保存時,這里其實B已經(jīng)打開了文件。但當(dāng)B再把文件保存回去時,這里已經(jīng)造成了數(shù)據(jù)的丟失,因為這里B用戶完全不知道它所打開的文件在它對其進行更改時,A用戶也更改了這個文件,所以最后B用戶保存更改時,用戶A的更新就被會丟失。
對于這樣的問題,一般的解決方案時當(dāng)一進程對文件進行操作時,首先對其它進行加鎖,意味著這里只有該進程有權(quán)對文件進行讀取,其它進程如果現(xiàn)在讀,是完全沒有問題,但如果這時有進程試圖想對其進行更新,會遭到操作拒絕,先前對文件進行加鎖的進程這時如果對文件的更新操作完畢,這就釋放獨占的標(biāo)識,這時文件又恢復(fù)到了可更改的狀態(tài)。接下來同理,如果那個進程在操作文件時,文件沒有加鎖,這時,它就可以放心大膽的對文件進行鎖定,獨自享用。
一般的方案會是:

復(fù)制代碼 代碼如下:

$fp=fopen('/tmp/lock.txt','w+');
if (flock($fp,LOCK_EX)){
    fwrite($fp,"Write something here\n");
    flock($fp,LOCK_UN);
}else{
    echo 'Couldn\'t lock the file !';
}
fclose($fp);

但在PHP中,flock似乎工作的不是那么好!在多并發(fā)情況下,似乎是經(jīng)常獨占資源,不即時釋放,或者是根本不釋放,造成死鎖,從而使服務(wù)器的cpu占用很高,甚至有時候會讓服務(wù)器徹底死掉。好像在很多l(xiāng)inux/unix系統(tǒng)中,都會有這樣的情況發(fā)生。所以使用flock之前,一定要慎重考慮。
那么就沒有解決方案了嗎?其實也不是這樣的。如果flock()我們使用得當(dāng),完全可能解決死鎖的問題。當(dāng)然如果不考慮使用flock()函數(shù),也同樣會有很好的解決方案來解決我們的問題。經(jīng)過我個人的搜集和總結(jié),大致歸納了解決方案有如下幾種。
方案一:對文件進行加鎖時,設(shè)置一個超時時間。大致實現(xiàn)如下:
復(fù)制代碼 代碼如下:

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);
}

超時設(shè)置為1ms,如果這里時間內(nèi)沒有獲得鎖,就反復(fù)獲得,直接獲得到對文件操作權(quán)為止,當(dāng)然。如果超時限制已到,就必需馬上退出,讓出鎖讓其它進程來進行操作。

方案二:不使用flock函數(shù),借用臨時文件來解決讀寫沖突的問題。大致原理如下:
(1)將需要更新的文件考慮一份到我們的臨時文件目錄,將文件最后修改時間保存到一個變量,并為這個臨時文件取一個隨機的,不容易重復(fù)的文件名。
(2)當(dāng)對這個臨時文件進行更新后,再檢測原文件的最后更新時間和先前所保存的時間是否一致。
(3)如果最后一次修改時間一致,就將所修改的臨時文件重命名到原文件,為了確保文件狀態(tài)同步更新,所以需要清除一下文件狀態(tài)。
(4)但是,如果最后一次修改時間和先前所保存的一致,這說明在這期間,原文件已經(jīng)被修改過,這時,需要把臨時文件刪除,然后返回false,說明文件這時有其它進程在進行操作。
實現(xiàn)代碼如下:

復(fù)制代碼 代碼如下:

$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);
  //說明有其它進程 在操作目標(biāo)文件,當(dāng)前進程被拒絕
        $success=false;
    }
    return $success;
}
$fp=cfopen('lock.txt','a+');
cfwrite($fp,"welcome to beijing.\n");
fclose($fp,'on');

對于上面的代碼所使用的函數(shù),需要說明一下:
(1)rename();重命名一個文件或一個目錄,該函數(shù)其實更像linux里的mv。更新文件或者目錄的路徑或名字很方便。但當(dāng)我在window測試上面代碼時,如果新文件名已經(jīng)存在,會給出一個notice,說當(dāng)前文件已經(jīng)存在。但在linux下工作的很好。
(2)clearstatcache();清除文件的狀態(tài).php將緩存所有文件屬性信息,以提供更高的性能,但有時,多進程在對文件進行刪除或者更新操作時,php沒來得及更新緩存里的文件屬性,容易導(dǎo)致訪問到最后更新時間不是真實的數(shù)據(jù)。所以這里需要使用該函數(shù)對已保存的緩存進行清除。

方案三:對操作的文件進行隨機讀寫,以降低并發(fā)的可能性。
在對用戶訪問日志進行記錄時,這種方案似乎被采用的比較多。先前需要定義一個隨機空間,空間越大,并發(fā)的的可能性就越小,這里假設(shè)隨機讀寫空間為[1-500],那么我們的日志文件的分布就為log1~到log500不等。每一次用戶訪問,都將數(shù)據(jù)隨機寫到log1~log500之間的任一文件。在同一時刻,有2個進程進行記錄日志,A進程可能是更新的log32文件,而B進程呢?則此時更新的可能就為log399.要知道,如果要讓B進程也操作log32,概率基本上為1/500,差不多約等于零。在需要對訪問日志進行分析時,這里我們只需要先將這些日志合并,再進行分析即可。使用這種方案來記錄日志的一個好處時,進程操作排隊的可能性比較小,可以使進程很迅速的完成每一次操作。

方案四:將所有要操作的進程放入一個隊列中。然后專門放一個服務(wù)完成文件操作。隊列中的每一個排除的進程相當(dāng)于第一個具體的操作,所以第一次我們的服務(wù)只需要從隊列中取得相當(dāng)于具體操作事項就可以了,如果這里還有大量的文件操作進程,沒關(guān)系,排到我們的隊列后面即可,只要愿意排,隊列的多長都沒關(guān)系。

對于以前幾種方案,各有各的好處!大致可能歸納為兩類:
(1)需要排隊(影響慢)比如方案一、二、四
(2)不需要排隊。(影響快)方案三
在設(shè)計緩存系統(tǒng)時,一般我們不會采用方案三。因為方案三的分析程序和寫入程序是不同步的,在寫的時間,完全不考慮到時候分析的難度,只管寫的行了。試想一下,如我們在更新一個緩存時,如果也采用隨機文件讀寫法,那么在讀緩存時似乎會增加很多流程。但采取方案一、二就完全不一樣,雖然寫的時間需要等待(當(dāng)獲取鎖不成功時,會反復(fù)獲?。?,但讀文件是很方便的。添加緩存的目的就是要減少數(shù)據(jù)讀取瓶頸,從而提高系統(tǒng)性能。
從上為個人經(jīng)驗和一些資料的總結(jié),有什么不對的地方,或者沒有談到的地方,歡迎各位同行指正。

相關(guān)文章

  • PHP.vs.JAVA

    PHP.vs.JAVA

    php在諸多方面都不如java優(yōu)異,那么php開發(fā)出的oa產(chǎn)品何以與java產(chǎn)品競爭呢?在于Php陣營普遍走的是低端路線,而java陣營走的是中高端路線。兩者之間交叉的區(qū)域較小。
    2016-04-04
  • 怎樣去閱讀一份php源代碼

    怎樣去閱讀一份php源代碼

    技術(shù)的快速進步,最好的途徑就是閱讀源代碼了。自己也閱讀了很多開源的程序,感覺方法很重要,好的方法可以達到事半功倍的效果。一下就是我自己的一些新的,希望對大家都有用。
    2009-08-08
  • PHP 加密與解密的斗爭

    PHP 加密與解密的斗爭

    PHP代碼的保護一直是許多公司關(guān)注的核心問題,例如知名的一些論壇vBulletin、Discuz!、PhpWind及近日很火的ShopEx等等
    2009-04-04
  • PHP 開源框架22個簡單簡介

    PHP 開源框架22個簡單簡介

    PHP 是一個被廣泛使用的來進行Web開發(fā)的腳本語言。雖然有很多其它可供選擇的Web開發(fā)語言,像:ASP 和Ruby,但是PHP是目前為止世界上最為流行的。
    2009-08-08
  • javascript 小型動畫組件與實現(xiàn)代碼

    javascript 小型動畫組件與實現(xiàn)代碼

    javascript 小型動畫組件與實現(xiàn)代碼,想要學(xué)習(xí)js動畫的朋友可以參考下。思路確實很實用。
    2010-06-06
  • 測試您的 PHP 水平的題目

    測試您的 PHP 水平的題目

    測試您的 PHP 水平的題目...
    2007-05-05
  • PHP程序員面試 切忌急功近利(更需要注重以后的發(fā)展)

    PHP程序員面試 切忌急功近利(更需要注重以后的發(fā)展)

    招聘一個程序員,唯一對你有意義的是他能寫出好程序的能力。 很少人像這樣去招人,他們更喜歡去挑剔程序員的個人癖好和性格缺點。
    2010-09-09
  • dedecms 制作模板中使用的全局標(biāo)記圖文教程

    dedecms 制作模板中使用的全局標(biāo)記圖文教程

    dedecms 制作模板中使用的全局標(biāo)記圖文教程...
    2007-03-03
  • php格式化工具Beautify PHP小小BUG

    php格式化工具Beautify PHP小小BUG

    Beautify PHP is written entirely in PHP. The program was tested with Linux and Windows, PHP 4.1.0 and PHP 4.3.1, but it should work on most systems running PHP.
    2008-04-04
  • 成為好程序員必須避免的5個壞習(xí)慣

    成為好程序員必須避免的5個壞習(xí)慣

    這篇文章主要介紹了成為好程序員的必須避免的5個壞習(xí)慣,寫的很好啊,小編總結(jié),不管做什么都要總結(jié)出自己的一套東西,需要的朋友可以參考下
    2014-07-07

最新評論