Redis鎖相關


Redis鎖相關

 

    君不見,高堂明鏡悲白發,朝如青絲暮成雪。

 

背景:面試的時候被問到有哪些鎖,很快脫口而出Volatile、Synchronized和ReentrantLock,也能講出他們之間的一些區別;當問到如在同一服務下同步鎖可以起作用,但是在不同的服務器上部署同一個微服務工程,然后用nginx作代理,很明顯,現在的線程鎖不管用了。分布式情況下如何保證線程同步?當時就答不上來了,分布式環境下我們需要換一把鎖,這把鎖必須和兩套甚至多套系統沒有任何的耦合度。可以使用Redis鎖實現分布式場景下的線程同步,使用Redies的API,如果key不存在,則設置一個key,這個key就是我們現在使用的一把鎖。每個線程到此處,先設置鎖,如果設置鎖失敗,則表明當前有線程獲取到了鎖,就返回。

一、單一服務器下的鎖

例如將商品的數量存到Redis中。每個用戶搶購前都需要到Redis中查詢商品數量(代替mysql數據庫。不考慮事務),如果商品數量大於0,則證明商品有庫存;然后我們在進行庫存扣減和接下來的操作;因為多線程並發問題,我們還需要在get()方法內部使用同步代碼塊,保證查詢庫存和減庫存操作的原子性。

 1 import lombok.AllArgsConstructor;  2 import lombok.extern.slf4j.Slf4j;  3 import org.springframework.data.redis.core.RedisTemplate;  4 import org.springframework.web.bind.annotation.GetMapping;  5 import org.springframework.web.bind.annotation.RequestHeader;  6 import org.springframework.web.bind.annotation.RequestMapping;  7 import org.springframework.web.bind.annotation.RestController;  8 
 9 @RestController 10 @AllArgsConstructor 11 @RequestMapping("/redis") 12 @Slf4j 13 public class TryRedisLock { 14 
15     private RedisTemplate<String, String> redisTemplate; 16 
17     @GetMapping(value = "/try/buy") 18     public String get(@RequestHeader(required = false) String userId) { 19         synchronized (this) {  // 單機同步
20             String bird = redisTemplate.opsForValue().get("bird"); 21             Integer count = Integer.valueOf(bird); 22             if (count > 0) { 23                 // 減庫存
24                 redisTemplate.opsForValue().set("bird", String.valueOf(count - 1)); 25                 log.info("用戶{}, 搶到了, {} 號商品!", userId, bird); 26  } 27             return "零庫存"; 28  } 29  } 30 }
View Code

二、分布式場景redis鎖

分布式場景下使用redis鎖需要注意如下幾個問題:

  • 一台服務器宕機,導致無法釋放鎖;可以在try-catch的finally中釋放鎖或者給每一把鎖加過期時間。
  • 任務線程未執行完畢但鎖已失效;可以延長鎖的過期時間,使用定時器防止key過期。
  • 使用Redisson框架簡化代碼;getLock()方法代替了Redis的setIfAbsent()lock()設置過期時間,最終我們在交易結束后釋放鎖;延長鎖的操作則由Redisson框架替我們完成,它會使用輪詢去查看key是否過期,在交易沒有完成時,自動重設Redis的key過期時間。
 1 import lombok.AllArgsConstructor;  2 import lombok.extern.slf4j.Slf4j;  3 import org.springframework.data.redis.core.RedisTemplate;  4 import org.springframework.web.bind.annotation.GetMapping;  5 import org.springframework.web.bind.annotation.RequestHeader;  6 import org.springframework.web.bind.annotation.RequestMapping;  7 import org.springframework.web.bind.annotation.RestController;  8 
 9 @RestController 10 @AllArgsConstructor 11 @RequestMapping("/redis") 12 @Slf4j 13 public class TryRedisLock { 14 
15     private RedisTemplate<String, String> redisTemplate; 16 
17 
18     @GetMapping(value = "/try/again/buy") 19     public String getJudge(@RequestHeader(required = false) String userId) { 20         // 每個線程到此處,先設置鎖
21         /**
22  * 使用Redies的API如果key不存在,則設置一個key。這個key就是我們現在使用的一把鎖 23  * 每個線程到此處,先設置鎖 24  * 如果設置鎖失敗,則表明當前有線程獲取到了鎖,就返回。 25          */
26         Boolean birdLock = redisTemplate.opsForValue().setIfAbsent("birdLock", ""); 27         if (!birdLock) { 28             return ""; 29  } 30         try { 31             String bird = redisTemplate.opsForValue().get("bird"); 32             Integer count = Integer.valueOf(bird); 33             if (count > 0) { 34                 redisTemplate.opsForValue().set("bird", String.valueOf(count - 1)); 35                 log.info("用戶{}, 搶到了, {} 號商品!", userId, bird); 36  } 37         } finally { 38             redisTemplate.delete("birdLock");  // 刪除鎖
39  } 40         return ""; 41  } 42 
43     @GetMapping(value = "/try/buy") 44     public String get(@RequestHeader(required = false) String userId) { 45         synchronized (this) {  // 單機同步
46             String bird = redisTemplate.opsForValue().get("bird"); 47             Integer count = Integer.valueOf(bird); 48             if (count > 0) { 49                 // 減庫存
50                 redisTemplate.opsForValue().set("bird", String.valueOf(count - 1)); 51                 log.info("用戶{}, 搶到了, {} 號商品!", userId, bird); 52  } 53             return "零庫存"; 54  } 55  } 56 }
View Code

 

 

 

 

君不見

         高堂明鏡悲白發

          朝如青絲暮成雪

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM