php基于redis的分布式鎖實例詳解
在使用分布式鎖進(jìn)行互斥資源訪問時候,我們很多方案是采用redis的實現(xiàn)。
固然,redis的單節(jié)點鎖在極端情況也是有問題的,假設(shè)你的業(yè)務(wù)允許偶爾的失效,使用單節(jié)點的redis鎖方案就足夠了,簡單而且效率高。
redis鎖失效的情況:
客戶端1從master節(jié)點獲取了鎖
master宕機(jī)了,存儲鎖的key還沒來得及同步到slave節(jié)點上
slave升級為master
客戶端2從新的master上獲取到同一個資源的鎖
于是,客戶端1和客戶端2同事持有了同一個資源的鎖,鎖的安全性被打破。
如果我們不考慮這種極端情況,需要實現(xiàn)一個基于單節(jié)點redis鎖的大致流程:
set cache_key random_seed NX PX 30000
上面這個set命令拆解開就是:
setnx cache_key random_seed expire cache_key 30
雖然這兩組命令執(zhí)行的效果一樣,但是第二個是非原子性操作,如果執(zhí)行了setnx成功,但是expire失敗的話,就會造成這個key一直存在了,無法釋放的情況。
redis的作者也指出,在使用單節(jié)點redis鎖的時候,設(shè)置一個隨機(jī)種子作為key的值是很有必要的,保證了一個客戶端釋放的鎖必須是自己所持有的那個鎖。假設(shè)獲取鎖時set的不是一個隨機(jī)數(shù),而是一個固定值,
那么可能會出現(xiàn)下面的情況:
客戶端1獲取鎖成功
客戶端1在某個操作上阻塞了很長時間
過期時間到了,鎖自動釋放(但是在客戶端1看來自己還是持有鎖中)
客戶端2獲取到了對應(yīng)同一個資源的鎖
客戶端1從阻塞中恢復(fù)了,釋放掉自己持有的鎖,也就是釋放掉了客戶端2持有的鎖
客戶端2的鎖被客戶端1是否,失去安全性。
釋放鎖的操作,很多人直接用del命令,這會有很大的問題,保證不了這個key是被加鎖人鎖刪。這時候需要用到隨機(jī)數(shù)了。
釋放鎖的操作有三步:
get 所持有鎖
判斷這個鎖是否自己所持有
刪除持有鎖
所以,這三步要保證原子性。用lua腳本來執(zhí)行,redis官方已經(jīng)提供腳本文件。
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
這段腳本在執(zhí)行的時候,需要把前面的隨機(jī)數(shù)作為argv[1] 的值傳進(jìn)去,把cache_key作為keys[1]的值傳進(jìn)去。
public class RedisLockHelper { @Resource private R2mClusterClient r2mClusterClient; /** * 類似于setNx的功能,同時設(shè)置過期時間為expire毫秒 * * @param key 加鎖key * @param value 確保在加鎖時間內(nèi)的唯一因子 * @param expire 過期時間的毫秒數(shù) * @return */ private String setLock(String key, String value, long expire) { return this.set(key, value, "NX", "PX", expire); } /** * 刪除指定key value * 如果 r2m中 key 對應(yīng)的value==value 返回 1 * 如果 r2m中 key 對應(yīng)的value!=value 返回 0 * * @param key * @return */ private boolean atomDelete(String key, String value) { List<String> values = new ArrayList<>(); values.add(value); String sb = "if redis.call('get',KEYS[1])==ARGV[1] then " + " return redis.call('del',KEYS[1]) " + " else " + " return 0" + " end"; if (this.eval(sb, key, values) == 1) { return true; } return false; } private Long eval(String mobel, String key, List<String> value) { return (Long) this.r2mClusterClient.eval(mobel, key, value); } private String set(String key, String value, String nxxx, String expx, long time) { return this.r2mClusterClient.set(key, value, nxxx, expx, time); } }
r2mClusterClient 就是jedis客戶端的封裝。
到此這篇關(guān)于php基于redis的分布式鎖實例詳解的文章就介紹到這了,更多相關(guān)php基于redis的分布式鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
thinkPHP實現(xiàn)上傳圖片及生成縮略圖功能示例
這篇文章主要介紹了thinkPHP實現(xiàn)上傳圖片及生成縮略圖功能,結(jié)合實例形式分析了thinkPHP圖片上傳及縮略圖設(shè)置、生成、保存、數(shù)據(jù)庫寫入等相關(guān)操作技巧,需要的朋友可以參考下2017-10-10php-fpm重啟導(dǎo)致的程序執(zhí)行中斷問題詳解
這篇文章主要給大家介紹了關(guān)于php-fpm重啟導(dǎo)致的程序執(zhí)行中斷問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04PHP獲取短鏈接跳轉(zhuǎn)后的真實地址和響應(yīng)頭信息的方法
這篇文章主要介紹了PHP獲取短鏈接跳轉(zhuǎn)后的真實地址和響應(yīng)頭信息的方法,本文使用get_headers函數(shù)實現(xiàn),需要的朋友可以參考下2014-07-07Laravel+Intervention實現(xiàn)上傳圖片功能示例
這篇文章主要介紹了Laravel+Intervention實現(xiàn)上傳圖片功能,結(jié)合實例形式分析了Intervention的安裝及圖片上傳功能的相關(guān)設(shè)置、使用與注意事項,需要的朋友可以參考下2019-07-07