天天看點

redis setnx 分布式鎖_Redis 分布式鎖PHP

Redis 分布式鎖的作用

在單機環境下,有個秒殺商品的活動,在短時間内,伺服器壓力和流量會陡然上升。這個就會存在并發的問題。想要解決并發需要解決一下問題

1、提高系統吞吐率也就是qps 每秒處理的請求書

解決問題一:采用記憶體型資料庫提高系統的qps

解決問題二:就要用到經常會遇到的鎖,例如MySQL 有讀鎖、寫鎖、排他鎖、悲觀鎖、樂觀鎖。不過這裡隻讨論redis來實作鎖

簡單版設定鎖

$redis = new Redis();
$redis->connect('127.0.0.1', 6379); //連接配接Redis
$expire = 10;//有效期10秒
$key = 'lock';//key
$value = time() + $expire;//鎖的值 = Unix時間戳 + 鎖的有效期
$lock = $redis->setnx($key, $value);
//判斷是否上鎖成功,成功則執行下步操作
if(!empty($lock))
{
//下步操作...
}
           

如果以這樣的簡單版設定鎖就能解決所有問題,未免也太小看 鎖 在程式中應用了。

按正常的操作示例基本上都是這樣寫的。但是這樣寫有一些問題

1、假如有10000 個請求通路了redis 不存在的鍵,這樣請求就是指接到了MySQL資料,造成CPU短時間内達到100%甚至當機。這樣場景俗稱緩存擊穿造成的緩存雪崩。

解決問題:引用reids setnx 方法的作用是,當設定的key 不存在時,設定新的值。這樣就避免了緩存擊穿的問題。檢測鍵的過期時間,避免産生死鎖

解決死鎖問題

$expire = 10;//有效期10秒
    $key = 'lock';//key
    $value = time() + $expire;//鎖的值 = Unix時間戳 + 鎖的有效期
    $status = true;
    while($status)
    {
        $lock = $redis->setnx($key, $value);
        if(empty($lock))
        {
            $value = $redis->get($key);
            if($value < time())
            {
                $redis->del($key);
            }
        }else{
            $status = false;
            //下步操作....
        }
    }
           

2、分布式叢集業務業務場景下,每台伺服器是獨立存在的。多台伺服器怎麼通過一個辨別來互相競争鎖呢。這裡就用到了分布式鎖

這裡簡單介紹一下,以MYSQL 的事務機制來延生。事務四個特性ACID,有四種隔離級别:為送出讀、已送出讀、可重複讀、串行化。這些特性都隻在單台伺服器上生效。到了分布式叢集了,資料在不同的伺服器上,緊靠事務很難保持資料的一緻性及隔離性,事務的作用就意義不大了。Redis也是如此。

正确的分布式鎖的打開方式

/**
     * 實作Redis分布鎖
     */
    $key        = 'demo';       //要更新資訊的緩存KEY
    $lockKey    = 'lock:'.$key; //設定鎖KEY
    $lockExpire = 10;           //設定鎖的有效期為10秒
    //擷取緩存資訊
    $result = $redis->get($key);
    //判斷緩存中是否有資料
    if(empty($result))
    {
        $status = TRUE;
        while ($status)
        {
            //設定鎖值為目前時間戳 + 有效期
            $lockValue = time() + $lockExpire;
            /**
             * 建立鎖
             * 試圖以$lockKey為key建立一個緩存,value值為目前時間戳
             * 由于setnx()函數隻有在不存在目前key的緩存時才會建立成功
             * 是以,用此函數就可以判斷目前執行的操作是否已經有其他程序在執行了
             * @var [type]
             */
            $lock = $redis->setnx($lockKey, $lockValue);
            /**
             * 滿足兩個條件中的一個即可進行操作
             * 1、上面一步建立鎖成功;
             * 2、   1)判斷鎖的值(時間戳)是否小于目前時間    $redis->get()
             *      2)同時給鎖設定新值成功    $redis->getset()
             */
            if(!empty($lock) || ($redis->get($lockKey) < time() && $redis->getSet($lockKey, $lockValue) < time() ))
            {
                //給鎖設定生存時間
                $redis->expire($lockKey, $lockExpire);
                //******************************
                //此處執行插入、更新緩存操作...
                //******************************
                //以上程式走完删除鎖
                //檢測鎖是否過期,過期鎖沒必要删除
                if($redis->ttl($lockKey))
                    $redis->del($lockKey);
                $status = FALSE;
            }else{
                /**
                 * 如果存在有效鎖這裡做相應處理
                 *      等待目前操作完成再執行此次請求
                 *      直接傳回
                 */
                sleep(2);//等待2秒後再嘗試執行操作
            }
        }
    }
           

結尾

文章從知識面的廣度(mysql)、示例代碼優缺點的簡介及應用的場景,差別于其他部落格文章。嘿嘿~

redis setnx 分布式鎖_Redis 分布式鎖PHP

更多精彩

敬請關注“PHP技術大全”微信公衆号

繼續閱讀