大廠的Redis鎖居然是這麼用的,難怪不會超賣!

1 本地鎖

經常使用的即 synchronize 或 Lock 等 JDK 自帶的鎖,只能鎖住當前進程,僅適用於單體架構服務。 而在分佈式多服務實例場景下必須使用分佈式鎖javascript

2 分佈式鎖

2.1 分佈式鎖的原理java

廁所佔坑理論redis

可同時去一個地方「佔坑」:架構

  • 佔到,就執行邏輯
  • 不然等待,直到釋放鎖

可經過自旋方式自旋分佈式

「佔坑」能夠去Redis、DB、任何全部服務都能訪問的地方。學習

2.2 分佈式鎖演進ui

一階段code

// 佔分布式鎖,去redis佔坑
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111");
if(lock) {
	//加鎖成功... 執行業務
	Map<String, List<Catelog2Vo>> dataFromDb = getDataFromDb();
	redisTemplate . delete( key: "lock");//fHßti
	return dataF romDb ;
} else {
	// 加鎖失敗,重試。synchronized()
	// 休眠100ms重試
	// 自旋
	return getCatalogJsonFromDbwithRedisLock();
}
關注公衆號:麒麟改bug,可獲取Redis實戰學習筆記一份。

問題場景blog

  • setnx佔好了坑,可是業務代碼異常或程序在執行過程當中宕機,即沒有執行成功刪除鎖邏輯,致使死鎖

解決方案:設置鎖的自動過時,即便沒有刪除,會自動刪除。進程

階段二

// 1. 佔分布式鎖,去redis佔坑
Boolean lock = redisTemplate.opsForValue().setIfAbsent( "lock", "110")
if(lock) {
	// 加鎖成功...執行業務
	
	// 忽然斷電
	
	// 2. 設置過時時間
	redisTemplate.expire("lock", timeout: 30, TimeUnit.SECONDS) ;
	Map<String, List<Catelog2Vo>> dataFromDb = getDataFromDb();
	//刪除鎖
	redisTemplate. delete( key; "lock");
	return dataFromDb;
} else {
	// 加鎖失敗...重試。 synchronized ()
	// 休眠100ms重試
	// 自旋的方式
	return getCatalogJsonF romDbWithRedisLock();
}

問題場景

  • setnx設置好,正要去設置過時時間,宕機,又死鎖

解決方案:設置過時時間和佔位必須是原子操做。redis支持使用setNxEx命令

階段三

// 1. 分佈式鎖佔坑
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "110", 300, TimeUnit.SECONDS);
if(lock)(
	// 加鎖成功,執行業務
	
	// 2. 設置過時時間,必須和加鎖一塊兒做爲原子性操做
	// redisTemplate. expire( "lock", з0, TimeUnit.SECONDS);
	Map<String, List<Catelog2Vo>> dataFromDb = getDataFromDb();
	// 刪除鎖
	redisTemplate.delete( key: "lock")
	return dataFromDb;
else {
	// 加鎖失敗,重試
	// 休眠100ms重試
	// 自旋
	return getCatalogJsonFromDbithRedislock()
}

階段四

已經拿到了 lockvalue ,有了 UUID,可是過時瞭如今!其餘人拿到所鎖設置了新值,因而 if 後將別人的鎖刪了!!也就是刪除鎖不是原子操做。

Map<String, List<Catelog2Vo>> dataFromDb = getDataFromDb();
String lockValue = redisTemplate.opsForValue().get("lock");
if(uuid.equals(lockValue)) {
	// 刪除我本身的鎖
	redisTemplate.delete("lock");
}

問題場景

  • 若是正好判斷是當前值,正要刪除鎖時,鎖已過時,別人已設置成功新值。那刪除的就是別人的鎖.
  • 解決方案

刪除鎖必須保證原子性。使用redis+Lua腳本。

階段五

  • 確保加鎖/解鎖都是原子操做
String script = 
	"if redis.call('get', KEYS[1]) == ARGV[1] 
		then return redis.call('del', KEYS[1]) 
	else 
		return 0 
	end";
	
	關注公衆號:麒麟改bug,可獲取Redis實戰學習筆記一份。

保證加鎖【佔位+過時時間】和刪除鎖【判斷+刪除】的原子性。 更難的事情,鎖的自動續期。

相關文章
相關標籤/搜索