99热99这里只有精品6国产,亚洲中文字幕在线天天更新,在线观看亚洲精品国产福利片 ,久久久久综合网

歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋(píng)果6袋
6
麥子學(xué)院

php并發(fā)控制中的獨(dú)占鎖

發(fā)布時(shí)間:2016-10-20 14:15  回復(fù):0  查看:2492   最后回復(fù):2016-10-20 14:15  

php開(kāi)發(fā)中,我們常常遇到并發(fā)問(wèn)題,那對(duì)于并發(fā)控制中的獨(dú)占鎖問(wèn)題我們?cè)趺唇鉀Q呢?一起來(lái)看看吧。

 

1.并發(fā)問(wèn)題

并發(fā)大家都知道是什么情況,這里說(shuō)的是并發(fā)多個(gè)請(qǐng)求搶占同一個(gè)資源,直接上實(shí)例吧

請(qǐng)求: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.分析

邏輯看來(lái)還可以,結(jié)果發(fā)現(xiàn)數(shù)據(jù)庫(kù)中寫(xiě)入了兩個(gè)同樣的請(qǐng)求結(jié)果,我看了記錄的時(shí)間戳,!居然是同一秒.

我用microtime(true) log一下兩個(gè)請(qǐng)求的時(shí)間差居然相差了0.0001s,就是說(shuō)$redis->setex($key,10,1);還沒(méi)執(zhí)行成功 第二個(gè)請(qǐng)求已經(jīng)get到跟第一個(gè)請(qǐng)求一樣的結(jié)果。這不就是傳說(shuō)中的并發(fā)搶占資源。這中情況 聽(tīng)過(guò)很多,在開(kāi)發(fā)過(guò)程中也沒(méi)刻意去模擬實(shí)驗(yàn)過(guò)。

 

3.解決

方案1:第一反應(yīng)就是要給處理過(guò)程加事務(wù)(數(shù)據(jù)庫(kù)是MySQL innoDB),加事務(wù)的結(jié)果就是 第一個(gè)請(qǐng)求成功了 第二個(gè)請(qǐng)求會(huì)執(zhí)行到后面撿查發(fā)現(xiàn)重了會(huì)回滾。其實(shí)mysql事務(wù)在保證數(shù)據(jù)一致性上是很ok的,但是通過(guò)回滾來(lái)保證唯一資源獨(dú)占代價(jià)太大,做過(guò)mysql事務(wù)測(cè)試測(cè)同學(xué)都知道,事務(wù)中的insert是已經(jīng)插進(jìn)去了,回滾之后才刪掉的。

方案2:還有一個(gè)選擇就是php中的文件獨(dú)占鎖,那就是說(shuō)這情況下我要新建 用戶數(shù) 任務(wù)數(shù)的文件來(lái)實(shí)現(xiàn)每個(gè)請(qǐng)求資源的獨(dú)占,如果獨(dú)占資源較少的話可選的解決辦法:

/**

     * 加鎖

     */

    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:發(fā)現(xiàn)$redis->setnx()可以提供原子操作的狀態(tài):相同的key執(zhí)行setnx之后沒(méi)過(guò)期或者沒(méi)del,再執(zhí)行會(huì)返回false。這就讓兩個(gè)以上的并發(fā)請(qǐng)求得到控制必須成功獲取鎖才能繼續(xù)。

/**

     *  加鎖

     */

    public function task_lock($taskid){

            $expire = 2;

             $lock_key ='task_get_reward_'.$this->uid.'_'.$taskid;

            $lock = $this->redis->setNX($lock_key , time());//設(shè)當(dāng)前時(shí)間

            if($lock){

                $this->redis->expire($lock_key,  $expire); //如果沒(méi)執(zhí)行完 2s鎖失效

            }

            if(!$lock){//如果獲取鎖失敗 檢查時(shí)間

                $time = $this->redis->get($lock_key);

                if(time() - $time  >=  $expire){//添加時(shí)間戳判斷為了避免expire執(zhí)行失敗導(dǎo)致死鎖 當(dāng)然可以用redis自帶的事務(wù)來(lái)保證

                    $this->redis->rm($lock_key);

                }

                $lock =  $this->redis->setNX($lock_key , time());

                if($lock){

                    $this->redis->expire($lock_key,  $expire); //如果沒(méi)執(zhí)行完 2s鎖失效

                }

            }

            return $lock;

        }

        /**

         *  解鎖

         */

        public function task_unlock($taskid){

            $this->set_redis();

            $lock_key = 'task_get_reward_'.$this->uid.'_'.$taskid;

            $this->redis->rm($lock_key);

        }

說(shuō)明下setNX expire 這兩個(gè)操作其實(shí)可以用redis事務(wù)來(lái)保證一致性

 

文章來(lái)源:魅族科技開(kāi)發(fā)團(tuán)隊(duì)

您還未登錄,請(qǐng)先登錄

熱門(mén)帖子

最新帖子

?