redis深入淺出分佈式鎖實現下篇
優化之UUID防誤刪
問題:刪除操作缺乏原子性。
場景:
index1執行刪除時,查詢到的lock值確實和uuid相等
uuid=v1
set(lock,uuid);
index1執行刪除前,lock剛好過期時間已到,被redis自動釋放,在redis中沒有瞭lock,沒有瞭鎖。
index2獲取瞭lock
index2線程獲取到瞭cpu的資源,開始執行方法
uuid=v2
set(lock,uuid);
index1執行刪除,此時會把index2的lock刪除
index1 因為已經在方法中瞭,所以不需要重新上鎖。index1有執行的權限。index1已經比較完成瞭,這個時候,開始執行
刪除的index2的鎖!
優化之LUA腳本保證刪除的原子性
@GetMapping("testLockLua") public void testLockLua() { //1 聲明一個uuid ,將做為一個value 放入我們的key所對應的值中 String uuid = UUID.randomUUID().toString(); //2 定義一個鎖:lua 腳本可以使用同一把鎖,來實現刪除! String skuId = "25"; // 訪問skuId 為25號的商品 100008348542 String locKey = "lock:" + skuId; // 鎖住的是每個商品的數據 // 3 獲取鎖 Boolean lock = redisTemplate.opsForValue().setIfAbsent(locKey, uuid, 3, TimeUnit.SECONDS); // 第一種: lock 與過期時間中間不寫任何的代碼。 // redisTemplate.expire("lock",10, TimeUnit.SECONDS);//設置過期時間 // 如果true if (lock) { // 執行的業務邏輯開始 // 獲取緩存中的num 數據 Object value = redisTemplate.opsForValue().get("num"); // 如果是空直接返回 if (StringUtils.isEmpty(value)) { return; } // 不是空 如果說在這出現瞭異常! 那麼delete 就刪除失敗! 也就是說鎖永遠存在! int num = Integer.parseInt(value + ""); // 使num 每次+1 放入緩存 redisTemplate.opsForValue().set("num", String.valueOf(++num)); /*使用lua腳本來鎖*/ // 定義lua 腳本 String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; // 使用redis執行lua執行 DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); redisScript.setScriptText(script); // 設置一下返回值類型 為Long // 因為刪除判斷的時候,返回的0,給其封裝為數據類型。如果不封裝那麼默認返回String 類型, // 那麼返回字符串與0 會有發生錯誤。 redisScript.setResultType(Long.class); // 第一個要是script 腳本 ,第二個需要判斷的key,第三個就是key所對應的值。 redisTemplate.execute(redisScript, Arrays.asList(locKey), uuid); } else { // 其他線程等待 try { // 睡眠 Thread.sleep(1000); // 睡醒瞭之後,調用方法。 testLockLua(); } catch (InterruptedException e) { e.printStackTrace(); } } }
Lua 腳本詳解:
項目中正確使用
定義key,key應該是為每個sku定義的,也就是每個sku有一把鎖。
String locKey ="lock:"+skuId; // 鎖住的是每個商品的數據 Boolean lock = redisTemplate.opsForValue().setIfAbsent(locKey, uuid,3,TimeUnit.SECONDS);
總結
加鎖
使用lua釋放鎖
重試
為瞭確保分佈式鎖可用,我們至少要確保鎖的實現同時滿足以下四個條件:
– 互斥性。在任意時刻,隻有一個客戶端能持有鎖。
– 不會發生死鎖。即使有一個客戶端在持有鎖的期間崩潰而沒有主動解鎖,也能保證後續其他客戶端能加鎖。
– 解鈴還須系鈴人。加鎖和解鎖必須是同一個客戶端,客戶端自己不能把別人加的鎖給解瞭。
– 加鎖和解鎖必須具有原子性
到此這篇關於redis深入淺出分佈式鎖實現下篇的文章就介紹到這瞭,更多相關redis分佈式鎖內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 關於SpringBoot 使用 Redis 分佈式鎖解決並發問題
- 聊聊使用RedisTemplat實現簡單的分佈式鎖的問題
- SpringBoot RedisTemplate分佈式鎖的項目實戰
- 詳解RedisTemplate下Redis分佈式鎖引發的系列問題
- redis深入淺出分佈式鎖實現上篇