1. 程式人生 > >php原子操作,文件鎖flock,數據庫事務

php原子操作,文件鎖flock,數據庫事務

監聽 做成 pri 信息 文件 open 由於 服務器 批量

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,數據庫事務