国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > PHP > 正文

php并發控制中的獨占鎖的例子

2024-05-04 21:50:15
字體:
來源:轉載
供稿:網友

1.并發問題

并發大家都知道是什么情況,這里說的是并發多個請求搶占同一個資源,直接上實例吧

請求:index.php?mod=a&action=b&taskid=6

處理:

  1. $key = "a_b::".$uid.'_'.$taskid
  2. $v = $redis->get($key); 
  3. if($v == 1){ 
  4.     $redis->setex($key,10,1); 
  5.     //處理邏輯省略 

2.分析

邏輯看來還可以,結果發現數據庫中寫入了兩個同樣的請求結果,我看了記錄的時間戳,天!居然是同一秒.

我用microtime(true) log一下兩個請求的時間差居然相差了0.0001s,就是說$redis->setex($key,10,1);還沒執行成功 第二個請求已經get到跟第一個請求一樣的結果。這不就是傳說中的并發搶占資源。這中情況 聽過很多,在開發過程中也沒刻意去模擬實驗過。

3.解決

方案1:第一反應就是要給處理過程加事務(數據庫是mysql innoDB),加事務的結果就是 第一個請求成功了 第二個請求會執行到后面撿查發現重了會回滾。其實mysql事務在保證數據一致性上是很ok的,但是通過回滾來保證唯一資源獨占代價太大,做過mysql事務測試測同學都知道,事務中的insert是已經插進去了,回滾之后才刪掉的。

方案2:還有一個選擇就是php中的文件獨占鎖,那就是說這情況下我要新建 用戶數 * 任務數的文件來實現每個請求資源的獨占,如果獨占資源較少的話可選的解決辦法:

  1. /** 
  2.      * 加鎖 
  3.      */ 
  4.     public function file_lock($filename){ 
  5.         $fp_key = sha1($filename); 
  6.         $this->fps[$fp_key] = fopen($filename'w+'); 
  7.         if($this->fps[$fp_key]){ 
  8.             return flock($this->fps[$fp_key], LOCK_EX|LOCK_NB); 
  9.         } 
  10.         return false; 
  11.     } 
  12.     /** 
  13.      * 解鎖 
  14.      */ 
  15.     public function file_unlock($filename){ 
  16.         $fp_key = sha1($filename); 
  17.         if($this->fps[$fp_key] ){ 
  18.             flock($this->fps[$fp_key] , LOCK_UN); 
  19.             fclose($this->fps[$fp_key] ); 
  20.         } 
  21.     } 

方案3:發現$redis->setnx()可以提供原子操作的狀態:相同的key執行setnx之后沒過期或者沒del,再執行會返回false。這就讓兩個以上的并發請求得到控制必須成功獲取鎖才能繼續。

  1. /** 
  2.      *  加鎖 
  3.      */ 
  4.     public function task_lock($taskid){ 
  5.             $expire = 2; 
  6.              $lock_key ='task_get_reward_'.$this->uid.'_'.$taskid
  7.             $lock = $this->redis->setNX($lock_key , time());//設當前時間 
  8.             if($lock){ 
  9.                 $this->redis->expire($lock_key,  $expire); //如果沒執行完 2s鎖失效 
  10.             } 
  11.             if(!$lock){//如果獲取鎖失敗 檢查時間 
  12.                 $time = $this->redis->get($lock_key); 
  13.                 if(time() - $time  >=  $expire){//添加時間戳判斷為了避免expire執行失敗導致死鎖 當然可以用redis自帶的事務來保證 
  14.                     $this->redis->rm($lock_key); 
  15.                 } 
  16.                 $lock =  $this->redis->setNX($lock_key , time()); 
  17.                 if($lock){ 
  18.                     $this->redis->expire($lock_key,  $expire); //如果沒執行完 2s鎖失效 
  19.                 } 
  20.             } 
  21.             return $lock
  22.         } 
  23.         /** 
  24.          *  解鎖 
  25.          */ 
  26.         public function task_unlock($taskid){ 
  27.             $this->set_redis(); 
  28.             $lock_key = 'task_get_reward_'.$this->uid.'_'.$taskid
  29.             $this->redis->rm($lock_key); 
  30.         } 
說明下setNX 和expire 這兩個操作其實可以用redis事務來保證一致性

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 兴和县| 泰来县| 逊克县| 内江市| 西城区| 富蕴县| 沿河| 古田县| 达日县| 玛曲县| 九江市| 晋州市| 理塘县| 响水县| 遂平县| 福贡县| 疏附县| 出国| 外汇| 武穴市| 井研县| 田阳县| 金门县| 莱州市| 万荣县| 宁强县| 岑溪市| 汉沽区| 长顺县| 云霄县| 峨眉山市| 项城市| 莫力| 清徐县| 宜昌市| 宽甸| 喜德县| 衡东县| 平顺县| 宁晋县| 长寿区|