在php開發
1.併發問題
併發大家都知道是什麼情況,這裡說的是併發多個請求搶占同乙個資源,直接上例項吧
請求:index.php?mod=a&action=b&taskid=6
處理:$key = "a_b::".$uid.'_'.$taskid;
$v = $redis->get($key);
if($v == 1){
$redis->setex($key,10,1);
//處理邏輯省略
2.分析
邏輯看來還可以,結果發現資料庫中寫入了兩個同樣的請求結果,我看了記錄的時間戳,天
!居然是同一秒.
我用microtime(true) log一下兩個請求的時間差居然相差了
0.0001s
,就是說
$redis->setex($key,10,1);
還沒執行成功 第二個請求已經
get到跟第乙個請求一樣的結果。這不就是傳說中的併發搶占資源。這中情況 聽過很多,在開發過程中也沒刻意去模擬實驗過。
3.解決
方案1:第一反應就是要給處理過程加事務
(資料庫是
mysql innodb)
,加事務的結果就是 第乙個請求成功了 第二個請求會執行到後面撿查發現重了會回滾。其實
mysql
事務在保證資料一致性上是很
ok的,但是通過回滾來保證唯一資源獨佔代價太大,做過
mysql
事務測試測同學都知道,事務中的
insert
是已經插進去了,回滾之後才刪掉的。
方案2:還有乙個選擇就是
php中的檔案獨佔鎖,那就是說這情況下我要新建 使用者數
* 任務數的檔案來實現每個請求資源的獨佔,如果獨佔資源較少的話可選的解決辦法:
* 加鎖
public function file_lock($filename){
$fp_key = sha1($filename);
$this->fps[$fp_key] = fopen($filename, 'w+');
if($this->fps[$fp_key]){
return flock($this->fps[$fp_key], lock_ex|lock_nb);
return false;
* 解鎖
public function file_unlock($filename){
$fp_key = sha1($filename);
if($this->fps[$fp_key] ){
flock($this->fps[$fp_key] , lock_un);
fclose($this->fps[$fp_key] );
方案3:發現
$redis->setnx()
可以提供原子操作的狀態:相同的
key執行
setnx
之後沒過期或者沒
del,再執行會返回
false
。這就讓兩個以上的併發請求得到控制必須成功獲取鎖才能繼續。
* 加鎖
public function task_lock($taskid){
$expire = 2;
$lock_key ='task_get_reward_'.$this->uid.'_'.$taskid;
$lock = $this->redis->setnx($lock_key , time());//設當前時間
if($lock){
$this->redis->expire($lock_key, $expire); //如果沒執行完
2s鎖失效
if(!$lock){//如果獲取鎖失敗 檢查時間
$time = $this->redis->get($lock_key);
if(time() - $time >= $expire){//新增時間戳判斷為了避免
expire
執行失敗導致死鎖 當然可以用
redis
自帶的事務來保證
$this->redis->rm($lock_key);
$lock = $this->redis->setnx($lock_key , time());
if($lock){
$this->redis->expire($lock_key, $expire); //如果沒執行完
2s鎖失效
return $lock;
* 解鎖
public function task_unlock($taskid){
$this->set_redis();
$lock_key = 'task_get_reward_'.$this->uid.'_'.$taskid;
$this->redis->rm($lock_key);
說明下setnx 和
expire
這兩個操作其實可以用
redis
事務來保證一致性
php 併發控制中的獨佔鎖
併發大家都知道是什麼情況,這裡說的是併發多個請求搶占同乙個資源,直接上例項吧 請求 index.php?mod a action b taskid 6 處理 key a b uid.taskid v redis get key if v 1 邏輯看來還可以,結果發現資料庫中寫入了兩個同樣的請求結果,...
JUC併發基石之AQS原始碼解析 獨佔鎖的釋放
juc併發基石之aqs原始碼解析 獨佔鎖的獲取public final boolean release int arg return false 獨佔鎖的涉及到兩個函式的呼叫 1.tryrelease arg 該方法由aqs的子類來實現釋放鎖的具體邏輯 2.unparksuccessor h 喚醒後...
併發程式設計核心框架AQS之獨佔鎖原理詳解
如何設計符合冪等性的高質量restful api 理解restful的冪等性,並且設計符合冪等規範的高質量restful api。http冪等方法,是指無論呼叫多少次都不會有不同結果的 http 方法。不管你呼叫一次,還是呼叫一百次,一千次,結果都是相同的。還是以之前的博文的例子為例。get tic...