一、分布式鎖的作用:
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的數量。
Redis實現分布式鎖 php
一 分布式鎖的作用 redis寫入時不帶鎖定功能,為防止多個程序同時進行乙個操作,出現意想不到的結果,so.對快取進行插入更新操作時自定義加鎖功能。二 redis的nx字尾命令 redis有一系列的命令,其特點是以nx結尾,nx的意思可以理解為 not exists 不存在 setnx命令 set ...
PHP實現Redis分布式鎖
鎖在我們的日常開發可謂用得比較多。通常用來解決資源併發的問題。特別是多機集群情況下,資源爭搶的問題。但是,很多新手在鎖的處理上常常會犯一些問題。今天我們來深入理解鎖。一 redis 鎖錯誤使用之一 我曾經見過有的專案把查詢結果儲存到 redis 當中時的偽 如下 redis new redis 12...
Redis 分布式鎖(PHP實現)
先來看看如果不用分布式鎖,所謂的電商庫存超賣是啥意思?大家看看下面的圖 這個圖,其實很清晰了,假設訂單系統部署兩台機器上,不同的使用者都要同時買10臺iphone,分別發了乙個請求給訂單系統。接著每個訂單系統例項都去資料庫里查了一下,當前iphone庫存是12臺。倆大兄弟一看,樂了,12臺庫存大於了...