這篇文章帶給大家的內容是關於php中原子操作與文件鎖flock的介紹(程式碼範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
php原子操作,檔案鎖定flock,資料庫事務
php沒有繼承posix標準支援的unix鎖定,只封裝了一個linux系統呼叫flock(信號量也能做成鎖),按理也是可以使用鎖機制的,雖然效率低一點。
php腳本是運行在fastcgi容器中,而fastcgi是多進程的,所以如果php程式存取了臨界資源,勢必造成程式結果的不正確性。
估計也要考慮下fastcgi容器的問題
問題描述:駭客用的工具刷我們的後台
取消訂單時會有退款,駭客並發取消訂單,導致多次退款
如果請求一個一個來,哪怕間隔100毫秒,也是沒有問題的
一個PHP處理過程是: 讀退款標誌,發現沒退款, 退款,然後設定已退款標誌
問題是多個請求同時到了,讀出來的退款標誌都是未退款,所以多個請求都退款了
同一個php文件,被同時請求多次,是同一時刻
用php檔案鎖flock 我們試了不行,還是用C 隊列
用C 監聽了一個端口,直接接收HTTP包,然後返回HTTP格式的包,PHP程序中用curl存取我這個C程式.
相當於遠端呼叫了,可以部署到其他伺服器做分散式了
很多時候,我們並沒有考慮我們php程式碼的平行能力,尤其是在我們的php程式碼對某個資源可讀可寫的時候。但這並不是說php的所有操作都是原子的,事務的,可並行的。由於php腳本是運行在fastcgi容器中,而fastcgi是多進程的,所以如果php程式存取了臨界資源,勢必造成程式結果的不正確性。
解決問題的方法是使用鎖定機制。 php沒有繼承posix標準支援的unix鎖定:例如記錄鎖定fcntl,線程鎖等,而只封裝了一個linux系統呼叫flock(信號量也能做成鎖),flock形式為flock($fp,$type),其中$fp為檔案句柄,而$type為:
/* 當一個檔案的開啟方式是可讀可寫的,通常需要加入檔案鎖定*/
1. LOCK_SH 共用鎖:
通常為進程向檔案請求讀取操作時需加上共享鎖定。共享鎖可支援任一進程間的讀取操作,如果寫一個加了共享鎖的檔案則進程阻塞進入SLEEP狀態值到共享鎖解鎖
2. LOCK_EX 獨佔鎖:
通常為進程在檔案的寫入操作加獨佔鎖,一旦檔案加上了該鎖,則其他任意程序存取該檔案時都會阻塞,直到解鎖為止。
3. LOCK_UN 解鎖:
為加鎖的檔案句柄解鎖
這樣的加鎖方式必然可以保證加鎖程式區塊的原子性,但同時也犧牲了程式的效率,因此,我們實際的程序中應該在程序的加鎖和解鎖代碼間嵌入盡量少的程序邏輯(尤其是獨佔鎖),保證程序盡快解鎖。
最後,附上加上鎖定機制以後的程序:
<?php $usrinfo = isset($_GET["usrinfo"])?$_GET["usrinfo"]:exit(1); $stinfo = isset($_GET["stinfo"])?$_GET["stinfo"]:exit(1); echo $stinfo; $pid = posix_getpid(); $fp = fopen(“usrinfo.txt”,”a+”); $num = rand(0,100000); flock($fp,LOCK_EX); fwrite($fp,”user:”.$usrinfo.” stinfo:”.$stinfo.”–”.$pid.”–”.$num.”\n”); fwrite($fp,”talking 1 — pid:$pid and num:$num\n”); flock($fp,LOCK_UN); fclose($fp);
普通情況運行該程序,產生正確的結果。
用什麼方法可以在業務批次作業時保證原子性呢?
例如:刪除多條文章,但在中間有一條已經被刪除了,假設這裡會出現錯誤,那如何讓整個操作回滾,並定位錯誤訊息呢?
資料庫的事務保證原子性但不能定位錯誤訊息,但遇到無法使用交易的場景,應該怎麼做呢?
利用資料庫的交易來做是最合理的,錯誤訊息可以記錄啊,有操作失敗拋出錯誤。
應用邏輯保證,就是每操作一次做下記錄,成功失敗都做下記錄。中間出錯,可以把成功的回滾。一般我們刪除是假刪除,所以很容易。若真刪除,記錄時要記錄完整資訊。
PHP用檔案鎖模擬進程鎖,實作原子運算
用PHP實作原子操作,而PHP本身並沒有提供進程鎖機制,用PHP檔鎖機制,透過檔鎖模擬程序鎖實現原子操作。
原子操作的程式碼之前,使用排他鎖開啟某個文件,程式碼如下:
$fp = fopen( LOCK_FILE_PATH, "r" ); if (!$fp) { echo "Failed to open the lock file!"; exit(1);//异常处理 } flock ( $fp, LOCK_EX );
原子操作的程式碼之後,對該文件解鎖,並關閉文件,程式碼如下:
flock ( $fp, LOCK_UN ); fclose ( $fp );
整體偽代碼為:
define("LOCK_FILE_PATH", "/tmp/lock"); if( !file_exists(LOCK_FILE_PATH) ){ $fp = fopen( LOCK_FILE_PATH, "w" ); fclose ( $fp ); } $fp = fopen( LOCK_FILE_PATH, "r" ); if (!$fp) { echo "Failed to open the lock file!"; exit(1);//异常处理 } flock ( $fp, LOCK_EX ); //此处添加原子操作代码 flock ( $fp, LOCK_UN ); fclose ( $fp );
以上便可實現PHP原子操作,避免衝突。
php原子運算與mysql原子運算
原子操作常用的方法就是透過資料回滾來實現,用PHP 來實現資料庫回滾操作相當簡單:
1, 建立資料庫連線
2, mysql_query('BEGIN'); 開啟事務
3, $SQL = "...";
mysql_query($SQL); 做對應的資料庫操作
4, 判斷回溯條件:
if(mysql_errno)
#{
print mysql_error();
mysql_query('ROLLBACK'); 出錯就回滾
exit();
}
5,可以重複上述步驟3 及步驟4 的操作, 開始的過程(中間可以加入其他操作,不限於資料庫更新,但是注意,最好不要讓一個事務時間過長,因為它鎖定所有你用到的表,會影響其他程序使用)
你也可以在幾條正確的sql更新語句後故意寫一句錯誤的,看看是否回滾了。
6, 結束回滾操作
mysql_query('COMMIT'); 能夠到這裡,代表上述資料庫操作都沒有錯,正式提交執行
這就是用PHP 實作原子運算的整個過程,需要特別注意的是建立支援資料回溯操作的表結構,
另外,除commit 外也有其它辦法可以結束回滾操作。
以上是php中原子操作與檔案鎖flock的介紹(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!