一般實現分布式鎖都有哪些方式?使用 redis 如何設計分布式鎖?使用 zk 來設計分布式鎖可以嗎?這兩種分布式鎖的實現方式哪種效率比較高?
其實一般問問題,都是這麼問的,先問問你 zk,然後其實是要過度到 zk 關聯的一些問題裡去,比如分布式鎖。因為在分布式系統開發中,分布式鎖的使用場景還是很常見的。
官方叫做redlock
演算法,是 redis 官方支援的分布式鎖演算法。
這個分布式鎖有 3 個重要的考量點:
redis 最普通的分布式鎖
第乙個最普通的實現方式,就是在 redis 裡建立乙個 key,這樣就算加鎖。
set my:lock 隨機值 nx px 30000
執行這個命令就 ok。
釋放鎖就是刪除 key ,但是一般可以用lua
指令碼刪除,判斷 value 一樣才刪除:
-- 刪除鎖的時候,找到 key 對應的 value,跟自己傳過去的 value 做比較,如果是一樣的才刪除。
if redis.call("get",keys[1]) == ar**[1] then
return redis.call("del",keys[1])
else
return 0
end
為啥要用隨機值呢?因為如果某個客戶端獲取到了鎖,但是阻塞了很長時間才執行完,比如說超過了 30s,此時可能已經自動釋放鎖了,此時可能別的客戶端已經獲取到了這個鎖,要是你這個時候直接刪除 key 的話會有問題,所以得用隨機值加上面的lua
指令碼來釋放鎖。
但是這樣是肯定不行的。因為如果是普通的 redis 單例項,那就是單點故障。或者是 redis 普通主從,那 redis 主從非同步複製,如果主節點掛了(key 就沒有了),key 還沒同步到從節點,此時從節點切換為主節點,別人就可以 set key,從而拿到鎖。
redlock 演算法
這個場景是假設有乙個 redis cluster,有 5 個 redis master 例項。然後執行如下步驟獲取一把鎖:
獲取當前時間戳,單位是毫秒;
跟上面類似,輪流嘗試在每個 master 節點上建立鎖,過期時間較短,一般就幾十毫秒;
嘗試在大多數節點上建立乙個鎖,比如 5 個節點就要求是 3 個節點n / 2 + 1
;
客戶端計算建立好鎖的時間,如果建立鎖的時間小於超時時間,就算建立成功了;
要是鎖建立失敗了,那麼就依次之前建立過的鎖刪除;
只要別人建立了一把分布式鎖,你就得不斷輪詢去嘗試獲取鎖。
zk 分布式鎖,其實可以做的比較簡單,就是某個節點嘗試建立臨時 znode,此時建立成功了就獲取了這個鎖;這個時候別的客戶端來建立鎖會失敗,只能註冊個***監聽這個鎖。釋放鎖就是刪除這個 znode,一旦釋放掉就會通知客戶端,然後有乙個等待著的客戶端就可以再次重新加鎖。
/**
* zookeepersession
* * @author bingo
* @since 2018/11/29
* */
public class zookeepersession catch (interruptedexception e)
system.out.println("zookeeper session established......");
} catch (exception e)
}/**
* 獲取分布式鎖
* * @param productid
*/public boolean acquiredistributedlock(long productid) catch (exception e)
zookeeper.create(path, "".getbytes(), ids.open_acl_unsafe, createmode.ephemeral);
return true;
} catch (exception ee) }}
return true;
}/**
* 釋放掉乙個分布式鎖
* * @param productid
*/public void releasedistributedlock(long productid) catch (exception e)
}/**
* 建立zk session的watcher
* * @author bingo
* @since 2018/11/29**/
private class zookeeperwatcher implements watcher
if (this.latch != null) }}
/*** 封裝單例的靜態內部類
* * @author bingo
* @since 2018/11/29**/
private static class singleton
public static zookeepersession getinstance()
}/**
* 獲取單例
* * @return
*/public static zookeepersession getinstance()
/*** 初始化單例的便捷方法
*/public static void init()
}
也可以採用另一種方式,建立臨時順序節點:
如果有一把鎖,被多個人給競爭,此時多個人會排隊,第乙個拿到鎖的人會執行,然後釋放鎖;後面的每個人都會去監聽排在自己前面的那個人建立的 node 上,一旦某個人釋放了鎖,排在自己後面的人就會被 zookeeper 給通知,一旦被通知了之後,就 ok 了,自己就獲取到了鎖,就可以執行**了。
public class zookeeperdistributedlock implements watcher catch (ioexception e) catch (keeperexception e) catch (interruptedexception e)
}public void process(watchedevent event)
if (this.latch != null)
}public void acquiredistributedlock() else
} catch (keeperexception e) catch (interruptedexception e)
}public boolean trylock()
//如果不是最小的節點,找到比自己小1的節點
int previouslockindex = -1;
for(int i = 0; i < locks.size(); i++)
}this.waitnode = locks.get(previouslockindex);
} catch (keeperexception e) catch (interruptedexception e)
return false;
}private boolean waitforlock(string waitnode, long waittime) throws interruptedexception, keeperexception
return true;
}public void unlock() catch (interruptedexception e) catch (keeperexception e)
}public class lockexception extends runtimeexception
public lockexception(exception e) }}
另外一點就是,如果是 redis 獲取鎖的那個客戶端 出現 bug 掛了,那麼只能等待超時時間之後才能釋放鎖;而 zk 的話,因為建立的是臨時 znode,只要客戶端掛了,znode 就沒了,此時就自動釋放鎖。
redis 分布式鎖大家沒發現好麻煩嗎?遍歷上鎖,計算時間等等......zk 的分布式鎖語義清晰實現簡單。
所以先不分析太多的東西,就說這兩點,我個人實踐認為 zk 的分布式鎖比 redis 的分布式鎖牢靠、而且模型簡單易用。
分布式 分布式鎖
本質是利用redis的setnx 方法的特性來加鎖,setnx 即key不存在則設定key,否則直接返回false,要求在分布式系統中使用同乙個redis服務,以下提供兩種解決方案 1 直接使用redistemplate 這其實並不能完全保證高併發下的安全問題,因為可能在鎖過期之後該執行緒尚未執行完...
分布式專題 分布式鎖
在傳統的單體應用架構中,遇到併發安全性問題時我們可以通過同步鎖synchronized,同步 塊,reentrantlock等方式都可以解決,但隨著業務的發展,單體應用架構不能滿足龐大的使用者請求量,於是分布式系統應用而生,在分布式系統中,由於每個系統都執行在不同的伺服器上,有著不同的jvm,所以j...
分布式 常見分布式框架
分布式協調系統 日誌複製系統 paxos演算法及其變體的實現,典型的有zookeeper etcd 分布式檔案系統 hdfs hadoop 分布式nosql redis hbase 訊息佇列 rabbitmq kafka,關注訊息的at least once,at most once,only on...