Redis實現分布式鎖 php

2021-08-07 18:07:54 字數 3522 閱讀 9432

一、分布式鎖的作用:

redis寫入時不帶鎖定功能,為防止多個程序同時進行乙個操作,出現意想不到的結果,so...對快取進行插入更新操作時自定義加鎖功能。

二、redis的nx字尾命令

redis有一系列的命令,其特點是以nx結尾,nx的意思可以理解為 not exists(不存在),setnx命令 (set if not exists) 可以理解為如果不存在則插入,redis分布式鎖的實現主要就是使用setnx命令。

三、實現原理

在程序請求執行操作前進行判斷,加鎖是否成功,加鎖成功允許執行下步操作;

如果不成功,則判斷鎖的值(時間戳)是否大於當前時間,如果大於當前時間,則獲取鎖失敗不允許執行下步操作;

如果鎖的值(時間戳)小於當前時間,並且getset命令獲取到的鎖的舊值依然小於當前時間,則獲取鎖成功允許執行下步操作;

如果鎖的值(時間戳)小於當前時間,並且getset命令獲取到的鎖的舊值大於當前時間,則獲取鎖失敗不允許執行下步操作;

四、$redis->setnx() 設定鎖

$expire = 10;//

有效期10秒

$key = 'lock';//

key$value = time() + $expire;//

鎖的值 = unix時間戳 + 鎖的有效期

$lock = $redis->setnx($key, $value

);//

判斷是否上鎖成功,成功則執行下步操作

if(!empty($lock

))

如果返回 1 ,則表示當前程序獲得鎖,並獲得了當前插入/更新快取的操作許可權。

如果返回 0,表示鎖已被其他程序獲取,這是程序可以返回結果或者等待當前鎖失效再請求。

五、解決死鎖

如果只用setnx命令設定鎖的話,如果當持有鎖的程序崩潰或刪除鎖失敗時,其他程序將無法獲取到鎖,問題就大了。

解決方法是在獲取鎖失敗的同時獲取鎖的值,並將值與當前時間進行對比,如果值小於當前時間說明鎖以過期失效,程序可運用redis的del命令刪除該鎖。

$expire = 10;//

有效期10秒

$key = 'lock';//

key$value = time() + $expire;//

鎖的值 = unix時間戳 + 鎖的有效期

$status = true

;while($status

)

}else

}

但是,簡單粗暴的用del命令刪除鎖再setnx命令上鎖也會出現問題。比如,程序1獲得鎖後崩潰或刪除鎖失敗,這時程序2檢測到鎖存在當已過期,用del命令刪除鎖並用setnx命令設定鎖,程序3也檢測到鎖過期,也用del命令刪除鎖也用setnx命令設定了鎖,這時程序2和程序3同時獲得了鎖。問題大了!

為了解決這個問題,這裡用到了redis的getset命令,getset命令在給鎖設定新值的同時返回鎖的舊值,這裡利用了getset命令同時獲取和賦值的特性,在此期間其他程序無法修改鎖的值。

例如:程序1獲得鎖後操作超時/崩潰/刪除鎖失敗,

程序2檢測到鎖已存在,但獲取鎖的值對比當前時間發現鎖已過期,

程序2通過getset命令重新給鎖賦予新的值,並獲取到的鎖的舊值,再次對比鎖的舊值與當前時間,如果鎖的舊值依然小於當前時間的話,這時程序2就可以忽略程序1餘留下的廢鎖進行下步操作了。

程序2完成下步操作後返回前應該刪除鎖,但在刪除鎖時可以先檢測鎖是否還未過期,未過期才做刪除操作,已過期的就沒必要在去刪除鎖了,因為很有可能其他程序檢測到鎖過期時已經去獲取鎖了。

這裡要說明的是,如果有其他程序在程序2之前獲取到鎖,那麼程序2將獲取鎖失敗,但是程序2在用getset獲取鎖的舊值時也賦予了鎖新的值,改寫了其他程序賦予鎖的超時值。看到這大家可能會有疑問了,程序2沒獲取到鎖怎麼能改變鎖的值呢?是的,程序2改變了鎖的原有值,但這一點小小的時間誤差帶來的影響是可以忽略。

以下是redis實現分布式鎖的完整php**:

<?php /**

* 實現redis分布鎖 */

$key = 'test'; //

要更新資訊的快取key

$lockkey = 'lock:'.$key; //

設定鎖key

$lockexpire = 10; //

設定鎖的有效期為10秒

//獲取快取資訊

$result = $redis->get($key

);//

判斷快取中是否有資料

if(empty($result

))else}}

實現分布式鎖用到的redis命令介紹:

setnx(key, value)

將key的值設為value,當且僅當key不存在。

若給定的key已經存在,則setnx不做任何動作。

setnx是」set if not exists」(如果不存在,則set)的簡寫。

返回值:

設定成功,返回1。

設定失敗,返回0。

get(key)

返回key所關聯的字串值。

如果key不存在則返回特殊值nil。

假如key儲存的值不是字串型別,返回乙個錯誤,因為get只能用於處理字串值。

返回值:

key的值。

如果key不存在,返回nil。

getset(key, value)

將給定key的值設為value,並返回key的舊值。

當key存在但不是字串型別時,返回乙個錯誤。

返回值:

返回給定key的舊值(old value)。

當key沒有舊值時,返回nil。

expire(key, seconds)

為給定key設定生存時間。

當key過期時,它會被自動刪除。

在redis中,帶有生存時間的key被稱作「易失的」(volatile)。

在低於2.1.3版本的redis中,已存在的生存時間不可覆蓋。

從2.1.3版本開始,key的生存時間可以被更新,也可以被persist命令移除。(詳情參見

返回值:

設定成功返回1。

當key不存在或者不能為key設定生存時間時(比如在低於2.1.3中你嘗試更新key的生存時間),返回0。

ttl(key)

返回給定key的剩餘生存時間(time to live)(以秒為單位)。

返回值:

key的剩餘生存時間(以秒為單位)。

當key不存在或沒有設定生存時間時,返回-1 。

del(key)

移除給定的乙個或多個key。

返回值:

被移除key的數量。

轉至:

PHP實現Redis分布式鎖

鎖在我們的日常開發可謂用得比較多。通常用來解決資源併發的問題。特別是多機集群情況下,資源爭搶的問題。但是,很多新手在鎖的處理上常常會犯一些問題。今天我們來深入理解鎖。一 redis 鎖錯誤使用之一 我曾經見過有的專案把查詢結果儲存到 redis 當中時的偽 如下 redis new redis 12...

Redis 分布式鎖(PHP實現)

先來看看如果不用分布式鎖,所謂的電商庫存超賣是啥意思?大家看看下面的圖 這個圖,其實很清晰了,假設訂單系統部署兩台機器上,不同的使用者都要同時買10臺iphone,分別發了乙個請求給訂單系統。接著每個訂單系統例項都去資料庫里查了一下,當前iphone庫存是12臺。倆大兄弟一看,樂了,12臺庫存大於了...

Redis實現分布式鎖 php

一 分布式鎖的作用 redis寫入時不帶鎖定功能,為防止多個程序同時進行乙個操作,出現意想不到的結果,so.對快取進行插入更新操作時自定義加鎖功能。二 redis的nx字尾命令 redis有一系列的命令,其特點是以nx結尾,nx的意思可以理解為 not exists 不存在 setnx命令 set ...