經常使用的即 synchronize 或 Lock 等 JDK 自帶的鎖,只能鎖住當前進程,僅適用於單體架構服務。 而在分佈式多服務實例場景下必須使用分佈式鎖javascript
2.1 分佈式鎖的原理java
廁所佔坑理論redis
可同時去一個地方「佔坑」:架構
可經過自旋方式自旋分佈式
「佔坑」能夠去Redis、DB、任何全部服務都能訪問的地方。ide
2.2 分佈式鎖演進學習
一階段ui
// 佔分布式鎖,去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實戰學習筆記一份。
問題場景3d
解決方案:設置鎖的自動過時,即便沒有刪除,會自動刪除。code
階段二
// 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(); }
問題場景
解決方案:設置過時時間和佔位必須是原子操做。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實戰學習筆記一份。
保證加鎖【佔位+過時時間】和刪除鎖【判斷+刪除】的原子性。 更難的事情,鎖的自動續期。