php並發控制中的獨佔鎖的例子

來源:互聯網
上載者:User

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事務來保證一致性

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.