學做日本菜的網(wǎng)站好泉州搜索推廣
redis 通過setnx實現(xiàn)的分布式鎖有問題
如圖:
解決的新的工具為(閃亮登場):redisson
redisson可重入鎖的原理
實現(xiàn)語言lua:
加鎖實現(xiàn)腳本語言:
釋放鎖的腳本語言:
加鎖的lua
-- 首先判斷這個鎖是否存在,也就是判斷key是否存在。不存在則直接加鎖,存在則判斷是否flied是否存在,
if(redis.call('EXISTS',KEYS[1]) == 0)thenredis.call('HSET',KEYS[1],ARGV[1],'1')redis.call('EXPIRE',KEYS[1],ARGV[2])return 1
end
if(redis.call('HEXISTS',KEYS[1],ARGV[1]) == 1)thenredis.call('HINCRBY',KEYS[1],ARGV[1],1)redis.call('EXPIRE',KEYS[1],ARGV[2])return 1;
end
return 0
釋放鎖的lua(比人覺得是在大于0的基礎上減一,但是我覺得應該是在大于1的基礎上減一。因為,在第一次加鎖的時候,就設置為1,如果有其他重入則++,第二次則為2,刪除順序的話,應該是第一次大于1不刪除,第二次釋放鎖等于1,也是最后一個鎖,則直接刪除了)
-- 刪除邏輯,如果key存在,則查找flied,如果flied的值大于1,則釋放鎖,并減1if(redis.call('HEXISTS',KEYS[1],ARGV[1]) == 1) thenlocal num = redis.call('HGET',KEYS[1],ARGV[1]);local count = tonumber(num)if(count >1) thenredis.call('HINCRBY',KEYS[1],ARGV[1],-1)redis.call('EXPIRE',KEYS[1],ARGV[2])return 1elseredis.call('del',KEYS[1])return 1end
end
return 0
1、redisson的重入機制:通過redis hash實現(xiàn)
2、redisson的可重試機制:對于一個線程去獲取鎖,如果ttl(key的剩余過期時間),如果等于null,則為沒有相對應的key,則可以加鎖成功。如果不為null則說明已經(jīng)key了,加鎖失敗。失敗之后就是等待。等待也不是死等(一直while循環(huán)),因為redis在釋放一個鍵的時候,會發(fā)布一個通知,其他線程一直等待這個通過,有了通知之后,再次判斷是否已經(jīng)過了等待時間(設置的一個線程最長的等待時間,如果超出則獲取鎖失敗)。沒有超過,則去獲取鎖,沒有獲取成功。判斷是否超過設置的等待時間。如果沒有超過則繼續(xù)等待,這個等待就是在while循環(huán)當中(while循環(huán)里也不是一直循環(huán),而是等待鎖釋放的通知)。
通知是發(fā)布訂閱模式 :訂閱:SUBSCRIBE mychannel(mychannel是訂閱的頻道) 發(fā)布:PUBLISH mychannel "Key deleted: mykey"(mychannel 是發(fā)布的頻道)。發(fā)布和訂閱是同一個頻道,當有key刪除,則redis發(fā)布這個頻道的通知,其他線程收到這個通知之后,則就會去獲取相應的鎖了。
3、redisson超時釋放:對于一個線程獲取鎖之后,key就會超時釋放,這樣就造成了并發(fā)的問題。為了解決這樣的問題,給每一個獲取鎖的線程增加一個定時的任務(TimeOut),如果key釋放的時間剩余key設置的釋放時間的三分之一的話,就重新給key重新設置超時釋放的值(這個值一直是原本的時間)。(看門狗機制)
4、主從節(jié)點:主節(jié)點(寫,然后同步給從節(jié)點),用戶的查詢都到從節(jié)點(主查)。當主節(jié)點宕機,就會出現(xiàn)主節(jié)點的數(shù)據(jù)還沒有同步到從節(jié)點,導致的一系列的問題。
? ? ? ? 解決方法:使用集群節(jié)點,全部都是node,每個node都可以讀寫。避免了因為一個主節(jié)點宕機,從節(jié)點沒有數(shù)據(jù)的情況。當是分布式鎖的時候,只有當所有的節(jié)點的都加鎖成功的時候,才會返回加鎖成功,使用的redisson的mulit的聯(lián)鎖。