久久r热视频,国产午夜精品一区二区三区视频,亚洲精品自拍偷拍,欧美日韩精品二区

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

PHP使用redis實(shí)現(xiàn)分布式鎖的示例詳解

瀏覽:40日期:2022-06-06 14:38:03
目錄
  • 什么是分布式鎖
  • 實(shí)現(xiàn)原理
  • php實(shí)現(xiàn)代碼

最近在做一個(gè)領(lǐng)券功能的時(shí)候,發(fā)現(xiàn)在一定并發(fā)下會(huì)出現(xiàn)重復(fù)領(lǐng)券的問題。使用度娘一頓搜索操作之后,發(fā)現(xiàn)可以使用分布式鎖來(lái)解決這個(gè)問題。

什么是分布式鎖

分布式鎖是控制分布式系統(tǒng)之間同步訪問共享資源的一種方式。在分布式系統(tǒng)中,常常需要協(xié)調(diào)他們的動(dòng)作。如果不同的系統(tǒng)或是同一個(gè)系統(tǒng)的不同主機(jī)之間共享了一個(gè)或一組資源,那么訪問這些資源的時(shí)候,往往需要互斥來(lái)防止彼此干擾來(lái)保證一致性,這個(gè)時(shí)候,便需要使用到分布式鎖。

實(shí)現(xiàn)原理

實(shí)現(xiàn)分布式鎖的原理很簡(jiǎn)單,就是需要有一把鎖,多個(gè)服務(wù)同時(shí)去獲取鎖,但是只有一個(gè)服務(wù)能獲取到鎖。獲取到鎖的服務(wù)就可以執(zhí)行自己的業(yè)務(wù),沒有獲取到鎖的其他服務(wù)需要等待獲取到鎖的服務(wù)業(yè)務(wù)執(zhí)行完成后釋放鎖,然后再次嘗試獲取鎖。

實(shí)現(xiàn)分布式的方案有很多種。如下

  • 基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)分布式鎖,比如mysql
  • 基于緩存實(shí)現(xiàn)分布式鎖,比如redis
  • 基于Zookeeper實(shí)現(xiàn)分布式鎖

這里我們使用redis來(lái)實(shí)現(xiàn)分布式鎖,在執(zhí)行業(yè)務(wù)之前先獲取一個(gè)key,如果key存在就說(shuō)明已經(jīng)有其他服務(wù)獲得鎖,這個(gè)時(shí)候需要等待或者返回系統(tǒng)繁忙。如果key不存在,說(shuō)明沒有其他服務(wù)獲取鎖,把這個(gè)key保存到redis,然后執(zhí)行業(yè)務(wù),等待業(yè)務(wù)執(zhí)行完就從redis中刪除這個(gè)key。

php實(shí)現(xiàn)代碼

<?php
 
class RedisLock
{
    protected $redis;
 
    public function __construct(){
    
$redis = new Redis();
$redis->connect("127.0.0.1",6379);
 
$this->redis = $redis;
    }
    public function getLock($key){
$value = $this->redis->get($key);
return $value;
    }
 
    public function setLock($key,$value){
$this->redis->set($key,$value);
    }
 
    public function delLock($key){
$lineNumber = $thid->redis->del($key);
return $lineNumber;
    }
}
 
$key = "your_lock_key";
$value = time();
 
$redisLock = new RedisLock();
$isLock = $redisLock->get($key);
if($isLock) {
    //已有鎖,直接返回,不往下執(zhí)行了
    return false;
}
 
//沒有鎖,加鎖
$redisLock->setLock($key,$value);
 
 
 
//todo 執(zhí)行業(yè)務(wù)邏輯
sleep(5);
 
// 解鎖
$redisLock->delLock($key);

使用ab進(jìn)行測(cè)試

 加鎖     
 加鎖     
 加鎖     
 加鎖     
 加鎖     
 加鎖     
 加鎖     
 加鎖     
 執(zhí)行業(yè)務(wù)     
 解鎖     
 解鎖     
 執(zhí)行業(yè)務(wù)     
 解鎖     
 執(zhí)行業(yè)務(wù)     
 解鎖     
 執(zhí)行業(yè)務(wù)     
 解鎖     
 解鎖     
 執(zhí)行業(yè)務(wù)     
 解鎖     
 加鎖     
 執(zhí)行業(yè)務(wù)     
 解鎖     
 加鎖     
 執(zhí)行業(yè)務(wù)     
 解鎖

從測(cè)試結(jié)果來(lái)看,發(fā)現(xiàn)有多個(gè)執(zhí)行業(yè)務(wù),并沒有完全鎖住。這個(gè)是因?yàn)槲覀冇玫氖莚edis的set命令。set 命令用于設(shè)置給定 key 的值。如果 key 已經(jīng)存儲(chǔ)其他值, SET 就覆寫舊值,且無(wú)視類型。這樣會(huì)導(dǎo)致很多服務(wù)都能加鎖成功,而我們想要的是只有一個(gè)服務(wù)能加鎖成功。

要解決這個(gè)問題,需要了解redis的另一個(gè)命令setnx。setnx 命令在指定的 key 不存在時(shí),為 key 設(shè)置指定的值。

<?php
 
class RedisLock
{
    protected $redis;
 
    public function __construct(){
    
$redis = new Redis();
$redis->connect("127.0.0.1",6379);
 
$this->redis = $redis;
    }
    public function getLock($key){
$value = $this->redis->get($key);
return $value;
    }
 
    public function setLock($key,$value){
return $this->redis->setnx($key,$value);
    }
 
    public function delLock($key){
$lineNumber = $thid->redis->del($key);
return $lineNumber;
    }
}
 
$key = "your_lock_key";
$value = time();
 
$redisLock = new RedisLock();
$isLock = $redisLock->get($key);
if($isLock) {
    //已有鎖,直接返回,不往下執(zhí)行了
    return false;
}
 
//沒有鎖,加鎖
$setLock = $redisLock->setLock($key,$value);
if(!$setLock) {
    //加鎖失敗
    return false;
}
 
 
//todo 執(zhí)行業(yè)務(wù)邏輯
sleep(5);
 
// 解鎖
$redisLock->delLock($key);

再次使用ab進(jìn)行測(cè)試

 加鎖      
 加鎖      
 加鎖      
 加鎖      
 加鎖      
 加鎖      
 加鎖      
 加鎖失敗      
 加鎖失敗      
 加鎖失敗      
 加鎖失敗      
 加鎖失敗      
 已鎖      
 已鎖      
 已鎖      
 執(zhí)行業(yè)務(wù)      
 解鎖

從測(cè)試結(jié)果來(lái)看,在未加鎖的狀態(tài)下,有多個(gè)服務(wù)同時(shí)獲取加鎖,但是只有一個(gè)加鎖成功, 其他的都是返回加鎖失敗,再后面的服務(wù)更是直接返回已鎖。由此可見,加鎖成功。

那么到此就結(jié)束了嗎?其實(shí)并不是的。假如在已加鎖的情況執(zhí)行業(yè)務(wù),在業(yè)務(wù)過程中因?yàn)橐恍┰虺霈F(xiàn)異常導(dǎo)致退出而沒有進(jìn)行解鎖,那么將造成死鎖,后面的所有服務(wù)都無(wú)法再次獲取鎖。為了解決這個(gè)問題,我們需要對(duì)鎖設(shè)置一個(gè)過期的時(shí)間,防止死鎖的發(fā)生。

<?php
 
class RedisLock
{
    protected $redis;
 
    public function __construct(){
    
$redis = new Redis();
$redis->connect("127.0.0.1",6379);
 
$this->redis = $redis;
    }
    public function getLock($key){
$value = $this->redis->get($key);
return $value;
    }
 
    public function setLock($key,$value,$second){
$setnx = $this->redis->setnx($key,$value);
if(!$setnx) {
    return $setnx;
}
$expire = $this->redis->expire($key,$second);
if(!$expire) {
    $this->redis->del($key);
}
 
return $expire;
    }
 
    public function delLock($key){
$lineNumber = $thid->redis->del($key);
return $lineNumber;
    }
}
 
$key = "your_lock_key";
$value = time();
 
$redisLock = new RedisLock();
$isLock = $redisLock->get($key);
if($isLock) {
    //已有鎖,直接返回,不往下執(zhí)行了
    return false;
}
 
//沒有鎖,加鎖
$second = 5;
$setLock = $redisLock->setLock($key,$value,$second);
if(!$setLock) {
    //加鎖失敗
    return false;
}
 
 
//todo 執(zhí)行業(yè)務(wù)邏輯
sleep(5);
 
// 解鎖
$redisLock->delLock($key);

以上就是PHP使用redis實(shí)現(xiàn)分布式鎖的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于PHP redis分布式鎖的資料請(qǐng)關(guān)注其它相關(guān)文章!

標(biāo)簽: PHP
主站蜘蛛池模板: 新巴尔虎右旗| 阿拉善右旗| 宁海县| 七台河市| 五家渠市| 缙云县| 潜山县| 兴海县| 老河口市| 上饶市| 肥城市| 都兰县| 万宁市| 山西省| 洛扎县| 兰考县| 黔西| 缙云县| 广南县| 沙湾县| 徐汇区| 海丰县| 永胜县| 循化| 祁东县| 金川县| 新和县| 蒙自县| 独山县| 从化市| 冕宁县| 长垣县| 庄河市| 六枝特区| 新乡市| 搜索| 武威市| 义马市| 陇南市| 黑龙江省| 巴彦淖尔市|