本文目錄一覽:
php單進程怎麼處理並發
方案一:使用文件鎖排它鎖
flock函數用於獲取文件的鎖,這個鎖同時只能被一個線程獲取到,其它沒有獲取到鎖的線程要麼阻塞,要麼獲取失敗
在獲取到鎖的時候,先查詢庫存,如果庫存大於0,則進行下訂單操作,減庫存,然後釋放鎖
方案二:使用Mysql資料庫提供的悲觀鎖
Innodb存儲引擎支持行級鎖,當某行數據被鎖定時,其他進程不能對這行數據進行操作
先查詢並鎖定行:select stock_num from table where id=1 for update
if(stock_num 0){
//下訂單
update table set stock_num=stock-1 where id=1
}
php 使用redis鎖限制並發訪問類示例
本文介紹了php
使用redis鎖限制並發訪問類,並詳細的介紹了並發訪問限制方法。
1.並發訪問限制問題
對於一些需要限制同一個用戶並發訪問的場景,如果用戶並發請求多次,而伺服器處理沒有加鎖限制,用戶則可以多次請求成功。
例如換領優惠券,如果用戶同一時間並發提交換領碼,在沒有加鎖限制的情況下,用戶則可以使用同一個換領碼同時兌換到多張優惠券。
偽代碼如下:
if
A(可以換領)
B(執行換領)
C(更新為已換領)
D(結束)
如果用戶並發提交換領碼,都能通過可以換領(A)的判斷,因為必須有一個執行換領(B)後,才會更新為已換領(C)。因此如果用戶在有一個更新為已換領之前,有多少次請求,這些請求都可以執行成功。
2.並發訪問限制方法
使用文件鎖可以實現並發訪問限制,但對於分散式架構的環境,使用文件鎖不能保證多台伺服器的並發訪問限制。
Redis是一個開源的使用ANSI
C語言編寫、支持網路、可基於內存亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。
本文將使用其setnx方法實現分散式鎖功能。setnx即Set
it
N**ot
eX**ists。
當鍵值不存在時,插入成功(獲取鎖成功),如果鍵值已經存在,則插入失敗(獲取鎖失敗)
RedisLock.class.PHP
?php
/**
*
Redis鎖操作類
*
Date:
2016-06-30
*
Author:
fdipzone
*
Ver:
1.0
*
*
Func:
*
public
lock
獲取鎖
*
public
unlock
釋放鎖
*
private
connect
連接
*/
class
RedisLock
{
//
class
start
private
$_config;
private
$_redis;
/**
*
初始化
*
@param
Array
$config
redis連接設定
*/
public
function
__construct($config=array()){
$this-_config
=
$config;
$this-_redis
=
$this-connect();
}
/**
*
獲取鎖
*
@param
String
$key
鎖標識
*
@param
Int
$expire
鎖過期時間
*
@return
Boolean
*/
public
function
lock($key,
$expire=5){
$is_lock
=
$this-_redis-setnx($key,
time()+$expire);
//
不能獲取鎖
if(!$is_lock){
//
判斷鎖是否過期
$lock_time
=
$this-_redis-get($key);
//
鎖已過期,刪除鎖,重新獲取
if(time()$lock_time){
$this-unlock($key);
$is_lock
=
$this-_redis-setnx($key,
time()+$expire);
}
}
return
$is_lock?
true
:
false;
}
/**
*
釋放鎖
*
@param
String
$key
鎖標識
*
@return
Boolean
*/
public
function
unlock($key){
return
$this-_redis-del($key);
}
/**
*
創建redis連接
*
@return
Link
*/
private
function
connect(){
try{
$redis
=
new
Redis();
$redis-connect($this-_config[‘host’],$this-_config[‘port’],$this-_config[‘timeout’],$this-_config[‘reserved’],$this-_config[‘retry_interval’]);
if(empty($this-_config[‘auth’])){
$redis-auth($this-_config[‘auth’]);
}
$redis-select($this-_config[‘index’]);
}catch(RedisException
$e){
throw
new
Exception($e-getMessage());
return
false;
}
return
$redis;
}
}
//
class
end
?
demo.php
?php
require
‘RedisLock.class.php’;
$config
=
array(
‘host’
=
‘localhost’,
‘port’
=
6379,
‘index’
=
0,
‘auth’
=
”,
‘timeout’
=
1,
‘reserved’
=
NULL,
‘retry_interval’
=
100,
);
//
創建redislock對象
$oRedisLock
=
new
RedisLock($config);
//
定義鎖標識
$key
=
‘mylock’;
//
獲取鎖
$is_lock
=
$oRedisLock-lock($key,
10);
if($is_lock){
echo
‘get
lock
successbr’;
echo
‘do
sth..br’;
sleep(5);
echo
‘successbr’;
$oRedisLock-unlock($key);
//
獲取鎖失敗
}else{
echo
‘request
too
frequentlybr’;
}
?
測試方法:
打開兩個不同的瀏覽器,同時在A,B中訪問demo.php
如果先訪問的會獲取到鎖
輸出
get
lock
success
do
sth..
success
另一個獲取鎖失敗則會輸出request
too
frequently
保證同一時間只有一個訪問有效,有效限制並發訪問。
為了避免系統突然出錯導致死鎖,所以在獲取鎖的時候增加一個過期時間,如果已超過過期時間,即使是鎖定狀態都會釋放鎖,避免死鎖導致的問題。
源碼下載地址:點擊查看
PHP開發中解決並發問題的幾種實現方法分析
方案一:使用文件鎖排它鎖
flock函數用於獲取文件的鎖,這個鎖同時只能被一個線程獲取到,其它沒有獲取到鎖的線程要麼阻塞,要麼獲取失敗
在獲取到鎖的時候,先查詢庫存,如果庫存大於0,則進行下訂單操作,減庫存,然後釋放鎖
方案二:使用隊列
將用戶的下單請求依次存入一個隊列中,後台用一個單獨的進程處理隊列中的下單請求
PHP如何使用文件鎖解決高並發問題
?php
//連接資料庫
$con=mysqli_connect(“192.168.2.186″,”root”,”root”,”test”);
//查詢商品數量是否大於0,大於0才能下單,並減少庫存
$fp = fopen(“lock.txt”, “r”);
//加鎖
if(flock($fp,LOCK_EX))
{
$res=mysqli_fetch_assoc(mysqli_query($con,’SELECT total FROM shop WHERE id=1 LIMIT 1′));
if($res[‘total’]0){mysqli_query($con,’UPDATE shop SET total=total-1 WHERE id=1′);}
//執行完成解鎖
flock($fp,LOCK_UN);
}
//關閉文件
fclose($fp);
unset($res);
mysqli_close($con);
?
原創文章,作者:PAZAX,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/128622.html