1. 數據庫樂觀鎖;數據庫
2. 基於Redis的分佈式鎖;less
3. 基於ZooKeeper的分佈式鎖。本篇博客將介紹第二種方式,基於Redis實現分佈式鎖。雖然網上已經有各類介紹Redis分佈式鎖實現的博客,然而他們的實現卻有着各類各樣的問題,爲了不誤人子弟,本篇博客將詳細介紹如何正確地實現Redis分佈式鎖。分佈式
首先,爲了確保分佈式鎖可用,咱們至少要確保鎖的實現同時知足如下四個條件:ui
互斥性。在任意時刻,只有一個客戶端能持有鎖。code
不會發生死鎖。即便有一個客戶端在持有鎖的期間崩潰而沒有主動解鎖,也能保證後續其餘客戶端能加鎖。get
具備容錯性。只要大部分的Redis節點正常運行,客戶端就能夠加鎖和解鎖。博客
解鈴還須繫鈴人。加鎖和解鎖必須是同一個客戶端,客戶端本身不能把別人加的鎖給解了。string
使用StackExchange.Redis 實現起來簡單得很io
/// <summary> /// 加鎖,若是鎖定成功,就去執行方法 /// </summary> public static bool LockTake(string key, string data, int seconds, int db = 0) { // key:用key來當鎖,由於key是惟一的。 // value:不少童鞋可能不明白,有key做爲鎖不就夠了嗎,爲何還要用到value?緣由就是咱們在上面講到可靠性時, // 分佈式鎖要知足第四個條件解鈴還須繫鈴人,經過給value賦值爲Guid.NewGuid().ToString(),咱們就知道這把鎖是哪一個請求加的了,在解鎖的時候就能夠有依據。 return GetDatabase(db).LockTake(key, data, (DateTime.Now.AddSeconds(seconds) - DateTime.Now)); } /// <summary> /// 解鎖 /// </summary> public static bool LockRelease(string key, string data, int db = 0) { return GetDatabase(db).LockRelease(key, data); }
調用實現class
string guid = Guid.NewGuid().ToString(); if (RedisHelper.LockTake(wechatOpenId, guid, 90, 15)) { try { // 執行方法 } catch (Exception e) { // 異常 } finally { RedisHelper.LockRelease(wechatOpenId, guid, 15); } } else { // 已鎖,沒法執行 return null; }