摘要:這中情況聽過很多,在開發過程中也沒刻意去模擬實驗過。這就讓兩個以上的并發請求得到控制必須成功獲取鎖才能繼續。
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事務來保證一致性
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/21958.html
并發大家都知道是什么情況,這里說的是并發多個請求搶占同一個資源,直接上實例吧 請求: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); //處理邏輯省略 } 邏輯看來還可以,結果...
摘要:鎖與很好的隔離使用者與實現者所需要關注的領域。那么這個就是包裝線程并且放入到隊列的過程實現的方法。也證實了就是獲取鎖的線程的節點。如果發生異常取消請求,也就是將當前節點重隊列中移除。 前言 自從JDK1.5后,jdk新增一個并發工具包java.util.concurrent,提供了一系列的并發工具類。而今天我們需要學習的是java.util.concurrent.lock也就是它下面的...
摘要:其二如果返回值等于表示當前線程獲取共享鎖成功,但它后續的線程是無法繼續獲取的,也就是不需要把它后面等待的節點喚醒。 在了解了AQS獨占鎖模式以后,接下來再來看看共享鎖的實現原理。 原文地址:http://www.jianshu.com/p/1161... 搞清楚AQS獨占鎖的實現原理之后,再看共享鎖的實現原理就會輕松很多。兩種鎖模式之間很多通用的地方本文只會簡單說明一下,就不在贅述了,...
摘要:前段時間的項目中遇到了并發操作阻塞問題,因為請求會使用存儲數據。不過,這只限于來自同一個客戶端的多個請求,也就是說,來自一個客戶端的請求并不會阻塞另一個客戶端的請求。這將大大增加頁面的響應時間。至此解決并發阻塞問題。 ??前段時間的項目中遇到了session并發操作阻塞問題,因為請求會使用Session存儲數據。 ??在同一個客戶端瀏覽器打開不同標簽進行測試的時候,請求一直處于等待狀態...
閱讀 3279·2021-11-18 10:02
閱讀 3446·2021-10-11 10:58
閱讀 3379·2021-09-24 09:47
閱讀 1124·2021-09-22 15:21
閱讀 3938·2021-09-10 11:10
閱讀 3281·2021-09-03 10:28
閱讀 1751·2019-08-30 15:45
閱讀 2143·2019-08-30 14:22