Redis鎖分析

基於Redis的鎖一般分單節點或集羣多節點二中情況,下面分別介紹一下。

Redis單節點(客戶端:Jedis)

一、正確的加鎖方式

jedis.set(String key, String value, String nxxx, String expx, int time)

第一個參數爲:key

第二個參數爲:值(一般爲一個隨機數,這也是有講究的,知道爲什麼?)

第三個參數爲:設置爲NX,代表key不存在操作,存在的情況下不處理返回,從而確保只有一個客戶端可以獲得鎖;

第四個參數爲:設置爲PX 用於標識指定超時時間

第五個參數爲:超時時間,單位:毫秒(爲什麼要指定超時時間?......要是我掛了,不能執行刪除命名,那是不是鎖就不釋放了!!)

注意:這裏爲什麼不用setNx呢?jedis的setnx不能把設置值和指定過期時間放在一起執行,即非原子性,有風險呀。

二、釋放鎖

String lua_script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(lua_script, Collections.singletonList(key), Collections.singletonList(獲取鎖時生成的隨機數));

if (RELEASE_SUCCESS.equals(result)) {
	return true;
}

1、Redis支持直接運行Lua腳本語言,更多lua見 https://www.runoob.com/lua/lua-tutorial.html

2、爲什麼不通過get--del等命令來處理呢?---> 因爲Lua可以確保原子性,這個命令在一個事務端中

 

既然是單節點,那對可用性就做不到保障了,集羣在這時就誕生了,那對於在集羣環境下分佈式鎖要注意什麼呢?

Redis官方推出了RedLock,我們對它認識一下

RedLock是通過同一個key和value,在種master節點上申請鎖,當在超過一半的節點上獲取時,即代表該進程成功獲取到了鎖。 

大致實現如下(假設這裏有5個redis master,鎖自動釋放時間(TTL)爲10秒):
1、開始獲取鎖量首先獲取當前時間戳;

2、客戶端進程用相同的key,value去所有的redis master 服務中獲取鎖,獲取鎖會設置一個超時時間(超時時間遠小於【鎖自動釋放時間】,防止master節點阻塞過長時間引起系統性風險),當超過獲取時間時,直接去下一個節點獲取鎖;

3、客戶端獲得所有可獲得的鎖的時間減去第一步記錄的時間戳,小於TTL且至少在3個master上成功獲取鎖,纔是真正的鎖成功;

4、若獲取鎖成功,則鎖的真正有效時間爲TTL減去(之前獲取鎖總消耗時間+系統時間波動);

5、如果客戶端獲取鎖失敗,在所有的結點上進行鎖釋放,否則會影響其它客戶端獲取鎖。釋放鎖時需判斷這個鎖的value是不是自己設置的,如果是才刪除。

 

 

RedLock性能及崩潰恢復的相關解決方法

1.如果redis沒有持久化功能,在clientA獲取鎖成功後,所有redis重啓,另一客戶端能夠再次獲取到鎖,這樣違法了鎖的排他互斥性;

2.如果啓動AOF永久化存儲,事情會好些, 舉例:當我們重啓redis後,由於redis過期機制是按照unix時間戳走的,所以在重啓後,然後會按照規定的時間過期,不影響業務;但是由於AOF同步到磁盤的方式默認是每秒-次,如果在一秒內斷電,會導致數據丟失,立即重啓會造成鎖互斥性失效;但如果同步磁盤方式使用Always(每一個寫命令都同步到硬盤)造成性能急劇下降;所以在鎖完全有效性和性能方面要有所取捨; 

3.有效解決既保證鎖完全有效性及性能高效及即使斷電情況的方法是redis同步到磁盤方式保持默認的每秒,在redis無論因爲什麼原因停掉後要等待TTL時間後再重啓(學名:延遲重啓) ;缺點是 在TTL時間內服務相當於暫停狀態;

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章