分布式鎖一般有三種實現方式:
1. 數據庫樂觀鎖;
2. 基於Redis的分布式鎖;
3. 基於ZooKeeper的分布式鎖。本篇博客將介紹第二種方式,基於Redis實現分布式鎖。雖然網上已經有各種介紹Redis分布式鎖實現的博客,然而他們的實現卻有着各種各樣的問題,為了避免誤人子弟,本篇博客將詳細介紹如何正確地實現Redis分布式鎖。
可靠性
首先,為了確保分布式鎖可用,我們至少要確保鎖的實現同時滿足以下四個條件:
互斥性。在任意時刻,只有一個客戶端能持有鎖。
不會發生死鎖。即使有一個客戶端在持有鎖的期間崩潰而沒有主動解鎖,也能保證后續其他客戶端能加鎖。
具有容錯性。只要大部分的Redis節點正常運行,客戶端就可以加鎖和解鎖。
解鈴還須系鈴人。加鎖和解鎖必須是同一個客戶端,客戶端自己不能把別人加的鎖給解了。
使用StackExchange.Redis 實現起來簡單得很
/// <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);
}
調用實現
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;
}
