精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

繼續項目實戰,集成Redis分布式鎖

存儲 存儲軟件 分布式 Redis
今天我們就來把基于Redis實現的分布式鎖,集成到我們的項目中,分布式鎖歷來都受到大家的關注。不管是工作中、面試中,分布式鎖永遠是個不老的話題,也希望大家能掌握此技能,便于大家日后能"升官發財"。

[[386548]]

今天我們就來把基于Redis實現的分布式鎖,集成到我們的項目中,分布式鎖歷來都受到大家的關注。不管是工作中、面試中,分布式鎖永遠是個不老的話題,也希望大家能掌握此技能,便于大家日后能"升官發財"。

分布式鎖

背景

為什么要有分布式鎖呢?不是已經有synchronized、ReantrantLock等相關鎖了嗎?

是的,我們在開發應用的時候,如果需要對某一個共享變量進行多線程同步訪問的時候,可以使用我們學到的鎖進行處理,并且可以完美的運行,毫無Bug!

注意:這是單機應用,后來業務發展,需要做集群,一個應用需要部署到幾臺機器上然后做負載均衡 :

 

上圖可以看到,變量A存在三個服務器內存中(這個變量A主要體現是在一個類中的一個成員變量,是一個有狀態的對象),如果不加任何控制的話,變量A同時都會在分配一塊內存,三個請求發過來同時對這個變量操作,顯然結果是不對的!即使不是同時發過來,三個請求分別操作三個不同內存區域的數據,變量A之間不存在共享,也不具有可見性,處理的結果也是不對的!

如果我們業務中確實存在這個場景的話,我們就需要一種方法解決這個問題!

為了保證一個方法或屬性在高并發情況下的同一時間只能被同一個線程執行,在傳統單體應用單機部署的情況下,可以使用并發處理相關的功能進行互斥控制。但是,隨著業務發展的需要,原單體單機部署的系統被演化成分布式集群系統后,由于分布式系統多線程、多進程并且分布在不同機器上,這將使原單機部署情況下的并發控制鎖策略失效,單純的應用并不能提供分布式鎖的能力。為了解決這個問題就需要一種跨機器的互斥機制來控制共享資源的訪問,這就是分布式鎖要解決的問題!

分布鎖的基本原理

分布式環境下,多臺機器上多個進程對同一個共享資源(數據、文件等)進行操作,如果不做互斥,就有可能出現“余額扣成負數”,或者“商品超賣”的情況。

為了解決這個問題,需要分布式鎖服務。首先,來看一下分布式鎖應該具備哪些條件。

  • 互斥性:在任意時刻,對于同一個鎖,只有一個客戶端能持有,從而保證一個共享資源同一時間只能被一個客戶端操作;
  • 安全性:即不會形成死鎖,當一個客戶端在持有鎖的期間崩潰而沒有主動解鎖的情況下,其持有的鎖也能夠被正確釋放,并保證后續其它客戶端能加鎖;
  • 可用性:當提供鎖服務的節點發生宕機等不可恢復性故障時,“熱備” 節點能夠接替故障的節點繼續提供服務,并保證自身持有的數據與故障節點一致。
  • 對稱性:對于任意一個鎖,其加鎖和解鎖必須是同一個客戶端,即客戶端 A 不能把客戶端 B 加的鎖給解了。

目前市面上,分布式鎖的實現方案大致有三種:

  1. 數據庫樂觀鎖;
  2. 基于分布式緩存實現的鎖服務,典型代表有 Redis 和基于 Redis 的 RedLock;
  3. 基于分布式一致性算法實現的鎖服務,典型代表有 ZooKeeper、Chubby 和 ETCD。

項目中,使用的最多的是后兩種,其實每種方案都各有利弊。

預防死鎖

我們看下面這個典型死鎖場景。

一個客戶端獲取鎖成功,但是在釋放鎖之前崩潰了,此時該客戶端實際上已經失去了對公共資源的操作權,但卻沒有辦法請求解鎖(刪除 Key-Value 鍵值對),那么,它就會一直持有這個鎖,而其它客戶端永遠無法獲得鎖。

我們的解決方案是:在加鎖時為鎖設置過期時間,當過期時間到達,Redis 會自動刪除對應的 Key-Value,從而避免死鎖。需要注意的是,這個過期時間需要結合具體業務綜合評估設置,以保證鎖的持有者能夠在過期時間之內執行完相關操作并釋放鎖。

另外,前面已經說了,在實際項目中我們都是使用后兩種方案,所以我們重點在后兩種方案上。說明 此文章是基于前面我們搞的項目繼續開展,同時把Redis已經集成到項目中了,所以此文中分布式鎖是基于Redis的實現。

基于Redis實現分布式鎖

先說一下使用Redis實現方案的思路:

  • setnx +expire+delete
  • setnx+lua
  • set key value px milliseconds nx

簡單版

創建分布鎖

  1. import org.slf4j.Logger; 
  2. import org.slf4j.LoggerFactory; 
  3. import org.springframework.data.redis.core.RedisTemplate; 
  4. import org.springframework.stereotype.Component; 
  5.  
  6. import javax.annotation.Resource; 
  7. import java.util.UUID; 
  8. import java.util.concurrent.TimeUnit; 
  9.  
  10. @Component 
  11. public class DistributedLockV1 { 
  12.  
  13.     private Logger logger = LoggerFactory.getLogger(DistributedLockV1.class); 
  14.  
  15.     @Resource 
  16.     private RedisTemplate redisTemplate; 
  17.  
  18.     public boolean lock(String businessKey) { 
  19.         boolean result = false
  20.         String uniqueValue = UUID.randomUUID().toString(); 
  21.         try { 
  22.             //@see <a href="http://redis.io/commands/setnx">Redis Documentation: SETNX</a> 
  23.             result = redisTemplate.opsForValue().setIfAbsent(businessKey, uniqueValue); 
  24.             if (!result) { 
  25.                 return false
  26.             } 
  27.             //設置key的有效期 
  28.             redisTemplate.expire(businessKey, 10, TimeUnit.SECONDS); 
  29.             return result; 
  30.         } catch (Exception ex) { 
  31.             logger.error("獲取鎖失敗", ex); 
  32.         } 
  33.         return result; 
  34.     } 
  35.  
  36.     public void unlock(String businessKey) { 
  37.         try { 
  38.             //delete 
  39.             redisTemplate.delete(businessKey); 
  40.         } catch (Exception ex) { 
  41.             logger.error("釋放鎖失敗", ex); 
  42.         } 
  43.     } 

就這樣,一個簡單的分布式鎖就實現了,但是這里會存在問題,問題也不是一定會出現,在特定的時刻還是會出現的。

下面,我們就來把這個分布式鎖應用到用戶賬戶余額扣減的功能中。

我們來創建一張用戶賬戶表,表中主要有userId和余額:

  1. CREATE TABLE `user_account` ( 
  2.   `id` bigint NOT NULL AUTO_INCREMENT, 
  3.   `user_id` bigint DEFAULT NULL
  4.   `balance` decimal(10,2) DEFAULT NULL
  5.   `create_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP
  6.   `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP
  7.   PRIMARY KEY (`id`) 
  8. ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 

然后創建UserAccountRepository接口。

  1. import com.tian.user.entity.UserAccount; 
  2. import org.springframework.data.jpa.repository.JpaRepository; 
  3. import org.springframework.data.jpa.repository.Modifying; 
  4. import org.springframework.data.jpa.repository.Query; 
  5. import org.springframework.stereotype.Repository; 
  6.  
  7. import java.math.BigDecimal; 
  8.  
  9. @Repository 
  10. public interface UserAccountRepository extends JpaRepository<UserAccount, Long> { 
  11.  
  12.     UserAccount findByUserId(Long userId); 
  13.     //通過userId更新余額 
  14.     @Modifying 
  15.     @Query("update UserAccount u set u.balance=?1 where  u.userId=?2"
  16.     void updateBalanceByUserId(BigDecimal balance, Long userId); 

創建UserAccountService和實現類,并實現其扣減方法:

  1. import com.tian.user.entity.UserAccount; 
  2.  
  3. import java.math.BigDecimal; 
  4.  
  5. public interface UserAccountService {  
  6.     /** 
  7.      * 扣減余額 
  8.      * @param userId 當前用戶userId 
  9.      * @param balance 當前需要減余額 
  10.      * @return 是否扣減成功 
  11.      */ 
  12.     boolean reduceBalance(Long userId, BigDecimal balance); 
  13. import com.tian.user.entity.UserAccount; 
  14. import com.tian.user.lock.DistributedLockV1; 
  15. import com.tian.user.repository.UserAccountRepository; 
  16. import com.tian.user.service.UserAccountService; 
  17. import org.springframework.stereotype.Service; 
  18. import org.springframework.transaction.annotation.Transactional; 
  19.  
  20. import javax.annotation.Resource; 
  21. import java.math.BigDecimal; 
  22. import java.util.Date
  23.  
  24. @Service 
  25. @Transactional(rollbackFor = Exception.class) 
  26. public class UserAccountServiceImpl implements UserAccountService { 
  27.     private Logger logger= LoggerFactory.getLogger(getClass()); 
  28.     @Resource 
  29.     private UserAccountRepository userAccountRepository; 
  30.     @Resource 
  31.     private DistributedLockV1 distributedLockV1;  
  32.      
  33.     @Override 
  34.     public boolean reduceBalance(Long userId, BigDecimal balance) { 
  35.         try { 
  36.             //把該賬戶給鎖住,使用userId作為key。 
  37.             boolean lock = distributedLockV1.lock(userId.toString()); 
  38.             //獲取鎖失敗,則直接返回扣減失敗 
  39.             if (!lock) { 
  40.                 return false
  41.             } 
  42.             UserAccount userAccount = userAccountRepository.findByUserId(userId); 
  43.             BigDecimal currBalance = userAccount.getBalance(); 
  44.             //校驗余額是否足夠扣減 
  45.             if (currBalance.compareTo(balance) > 0) { 
  46.                 BigDecimal newBalance = currBalance.subtract(balance); 
  47.                 //扣減余額 
  48.                 userAccountRepository.updateBalanceByUserId(newBalance, userId); 
  49.                 return true
  50.             } 
  51.         }catch(Exception ex){ 
  52.             logger.error("余額扣減失敗", ex); 
  53.         } finally { 
  54.             //釋放鎖 
  55.             distributedLockV1.unlock(userId.toString()); 
  56.         } 
  57.         return false
  58.     } 

到此,簡單版的分布式鎖,以及如何使用,這里就已經搞完了。下面我們來理一下思路:

1.使用setnx(set not exist),就是如何set的這個key在redis不存在就返回true,否則返回false。

2.對已經set的key設置有效期,使用expire設置有效期。

3.校驗我們的可用余額是否足夠扣減,不夠就直接結束并使用delete刪除redis中的key。

4.扣減余額,更新數據庫余額值。

5.刪除key,delete redis中key。

那么,問題來了,第一步、第二步都成功了。但假如第三步查詢余額、扣減余額耗時20秒了,上面我們對Redis中key的有效期設置的10秒,也就是超時了,key過期了,并且在10秒到20秒之間又有其他線程來獲取到鎖了,然后此時把其他線程拿到的鎖給刪了,把其他線程的鎖給解了。此時,不就亂了嗎?

這也是面試中常被問使用redis做分布式鎖,業務超時了怎么辦?

升級版

我們可以把每次key對應的value返回,當釋放鎖的時候,判斷當前key對應的value是否是當前手里持有的value。

然后,我們針對上面的進行修改一版。

  1. import org.slf4j.Logger; 
  2. import org.slf4j.LoggerFactory; 
  3. import org.springframework.data.redis.core.RedisTemplate; 
  4. import org.springframework.stereotype.Component; 
  5.  
  6. import javax.annotation.Resource; 
  7. import java.util.UUID; 
  8. import java.util.concurrent.TimeUnit; 
  9.  
  10. @Component 
  11. public class DistributedLockV1 { 
  12.     private Logger logger = LoggerFactory.getLogger(DistributedLockV1.class); 
  13.     @Resource 
  14.     private RedisTemplate redisTemplate; 
  15.  
  16.     public String lockV2(String businessKey) { 
  17.         boolean result = false
  18.         String uniqueValue = UUID.randomUUID().toString(); 
  19.         try { 
  20.             result = redisTemplate.opsForValue().setIfAbsent(businessKey, uniqueValue); 
  21.             if (!result) { 
  22.                 return null
  23.             } 
  24.             redisTemplate.expire(businessKey, 100, TimeUnit.SECONDS); 
  25.             return uniqueValue; 
  26.         } catch (Exception ex) { 
  27.             logger.error("獲取鎖失敗", ex); 
  28.         } 
  29.         return null
  30.     } 
  31.  
  32.     public void unlockV2(String businessKey, String businessValue) { 
  33.         try { 
  34.             Object value = redisTemplate.opsForValue().get(businessKey); 
  35.             if (value == null) { 
  36.                 return false
  37.             } 
  38.             //當前key在redis中value和當前線程手里持有的是否一致 
  39.             if (!businessValue.equals(value)) { 
  40.                 //不一致,證明被其他線程獲取了 
  41.                 logger.info("key={}釋放鎖失敗嗎,該鎖已被其他線程獲取",businessKey); 
  42.                 return false
  43.             } 
  44.             redisTemplate.delete(businessKey); 
  45.             logger.info("key={}釋放鎖成功",businessKey); 
  46.         } catch (Exception ex) { 
  47.             logger.error("釋放鎖失敗", ex); 
  48.         } 
  49.     } 

這里比簡單版多了一個判斷,判斷持有鎖的線程是否為當前線程。盡管使用隨機字符串的 value來判斷是否為當前線程,但是在釋放鎖時(delete方法),還是無法做到原子操作,比如進程 A 執行完業務邏輯,在準備釋放鎖時,恰好這時候進程 A 的鎖自動過期時間到了,而另一個進程 B 獲得鎖成功,然后 B 還沒來得及執行,進程 A 就執行了 delete(key) ,釋放了進程 B 的鎖.... ,因此需要配合 Lua 腳本釋放鎖。

setnx+Lua腳本Lua 是一種輕量小巧的腳本語言,用標準 C 語言編寫并以源代碼形式開放, 其設計目的是為了嵌入應用程序中,從而為應用程序提供靈活的擴展和定制功能。

Lua 提供了交互式編程模式。我們可以在命令行中輸入程序并立即查看效果。

lua腳本優點:

  • 減少網絡開銷:原先多次請求的邏輯放在 redis 服務器上完成。使用腳本,減少了網絡往返時延
  • 原子操作:Redis會將整個腳本作為一個整體執行,中間不會被其他命令插入(想象為事務)
  • 復用:客戶端發送的腳本會永久存儲在Redis中,意味著其他客戶端可以復用這一腳本而不需要使用代碼完成同樣的邏輯

在resources目錄下創建一個redis-lock.lua文件。填入內容:

  1. if redis.call('get', KEYS[1]) == ARGV[1] 
  2.     then 
  3.         return redis.call('del', KEYS[1]) 
  4.     else 
  5.         return 0 
  6. end 

這段代碼的意思。就是通過key獲得其在redis中value,然后使用當前線程手里的value與之對比,一樣則刪除redis這個key。刪除返回1,否則返回0表示什么沒做。

Redis鎖代碼塊如下:

  1. import org.slf4j.Logger; 
  2. import org.slf4j.LoggerFactory; 
  3. import org.springframework.core.io.ClassPathResource; 
  4. import org.springframework.data.redis.core.RedisTemplate; 
  5. import org.springframework.data.redis.core.script.DefaultRedisScript; 
  6. import org.springframework.scripting.support.ResourceScriptSource; 
  7. import org.springframework.stereotype.Component; 
  8.  
  9. import javax.annotation.PostConstruct; 
  10. import javax.annotation.Resource; 
  11. import java.util.ArrayList; 
  12. import java.util.List; 
  13. import java.util.UUID; 
  14. import java.util.concurrent.TimeUnit; 
  15.  
  16. @Component 
  17. public class DistributedLockV1 { 
  18.  
  19.     private Logger logger = LoggerFactory.getLogger(DistributedLockV1.class); 
  20.  
  21.     @Resource 
  22.     private RedisTemplate redisTemplate; 
  23.  
  24.     private DefaultRedisScript<Long> script; 
  25.  
  26.     @PostConstruct 
  27.     public void init() { 
  28.         script = new DefaultRedisScript<Long>(); 
  29.         script.setResultType(Long.class); 
  30.         script.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis-lock.lua"))); 
  31.     } 
  32.  
  33.     public String lockV3(String key) { 
  34.         String value = UUID.randomUUID().toString().replace("-"""); 
  35.  
  36.         /* 
  37.          * setIfAbsent <=> SET key value [NX] [XX] [EX <seconds>] [PX [millseconds]] 
  38.          * set expire time 5 mins 
  39.          */ 
  40.         Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, value, 10000, TimeUnit.MILLISECONDS); 
  41.         if (flag) { 
  42.             return value; 
  43.         } 
  44.         return null
  45.     } 
  46.  
  47.     public void unlockV3(String key, String value) { 
  48.         /** 業務邏輯處理完畢,釋放鎖 **/ 
  49.         String lockValue = (String) redisTemplate.opsForValue().get(key); 
  50.         if (lockValue != null && lockValue.equals(value)) { 
  51.             System.out.println("lockValue========:" + lockValue); 
  52.             List<String> keys = new ArrayList<>(); 
  53.             keys.add(key); 
  54.             Object execute = redisTemplate.execute(script, keys, lockValue); 
  55.             System.out.println("execute執行結果,1表示執行del,0表示未執行 ===== " + execute); 
  56.             logger.info("{} 解鎖成功,結束處理業務"key); 
  57.             return
  58.         } 
  59.         logger.info("key={}釋放鎖失敗"key); 
  60.     } 

最后我們再次執行罰款扣減,日志輸出:

  1. lockValue========:199740e62c184a6a9897f9c95e720b4d 
  2. execute執行結果,1表示執行del,0表示未執行 ===== 1 
  3. 2021-03-09 19:03:51.592  INFO 6692 --- [nio-8080-exec-4] com.tian.user.lock.DistributedLockV1     : 1 解鎖成功,結束處理業務 

到此,setnx+Lua 這種方案我們已經實現了。如果對此有懷疑的,是好事,建議創建多個線程去調用罰款扣減這個service方法,看看器是否會出現問題。

「注意」

setnx在redis較低的版本里是沒有的,后面才引入的。其實我們也可以使用set命令來解決setnx,另外還可以加過期時間,整體命令為

  1. set key value nx px xxx 

value 最好是隨機字符串,這樣可以防止業務代碼執行時間超過設置的鎖自動過期時間,而導致再次釋放鎖時出現釋放其他進程鎖的情況。

setnx 瑣最大的缺點就是它加鎖時只作用在一個 Redis 節點上,即使 Redis 通過 Sentinel(哨崗、哨兵) 保證高可用,如果這個 master 節點由于某些原因發生了主從切換,那么就會出現鎖丟失的情況,下面是個例子:

  1. 在 Redis 的 master 節點上拿到了鎖;
  2. 但是這個加鎖的 key 還沒有同步到 slave 節點;
  3. master 故障,發生故障轉移,slave 節點升級為 master節點;
  4. 上邊 master 節點上的鎖丟失。

有的時候甚至不單單是鎖丟失這么簡單,新選出來的 master 節點可以重新獲取同樣的鎖,出現一把鎖被拿兩次的場景。

由此可知,鎖被獲取兩次,肯定不能滿足安全性了。

盡管前兩種方案不是很如意,總是有些問題,但也有被部分企業采用,下面我們就來看基于Redis來實現分布式鎖更高級的版本。

高級版 Redisson + RedLock

Redisson 是 java 的 Redis 客戶端之一,是 Redis 官網推薦的 java 語言實現分布式鎖的項目。

Redisson 提供了一些 api 方便操作 Redis。因為本文主要以鎖為主,所以接下來我們主要關注鎖相關的類,以下是 Redisson 中提供的多樣化的鎖:

  • 可重入鎖(Reentrant Lock)
  • 公平鎖(Fair Lock)
  • 聯鎖(MultiLock)
  • 紅鎖(RedLock)
  • 讀寫鎖(ReadWriteLock)
  • 信號量(Semaphore) 等等

總之,管你了解不了解,反正 Redisson 就是提供了一堆鎖... 也是目前大部分公司使用 Redis 分布式鎖最常用的一種方式。

整體加鎖和解鎖的代碼結構如下:

  1. RLock lock = redissonClient.getLock("xxx"); 
  2.  
  3. lock.lock(); 
  4.  
  5. try { 
  6.     ... 
  7. } finally { 
  8.     lock.unlock(); 

其實,加鎖和解鎖的磁層也是使用Lua腳本來實現的,有興趣的朋友可以去翻看一下器底層源碼。

由于篇幅問題,還涉及到Redis集群,所以這里就給出加鎖和解鎖的流程圖,僅供參考。

「加鎖過程」

 

「解鎖過程」

 

總結

通常我們為了實現 Redis 的高可用,一般都會搭建 Redis 的集群模式,比如給 Redis 節點掛載一個或多個 slave 從節點,然后采用哨兵模式進行主從切換。但由于 Redis 的主從模式是異步的,所以可能會在數據同步過程中,master 主節點宕機,slave 從節點來不及數據同步就被選舉為 master 主節點,從而導致數據丟失,大致過程如下:

  1. 用戶在 Redis 的 master 主節點上獲取了鎖;
  2. master 主節點宕機了,存儲鎖的 key 還沒有來得及同步到 slave 從節點上;
  3. slave 從節點升級為 master 主節點;
  4. 用戶從新的 master 主節點獲取到了對應同一個資源的鎖,同把鎖獲取兩次。

ok,然后為了解決這個問題,Redis 作者提出了 RedLock 算法,步驟如下(五步):

在下面的示例中,我們假設有 5 個完全獨立的 Redis Master 節點,他們分別運行在 5 臺服務器中,可以保證他們不會同時宕機。

獲取當前 Unix 時間,以毫秒為單位。

依次嘗試從 N 個實例,使用相同的 key 和隨機值獲取鎖。在步驟 2,當向 Redis 設置鎖時,客戶端應該設置一個網絡連接和響應超時時間,這個超時時間應該小于鎖的失效時間。例如你的鎖自動失效時間為 10 秒,則超時時間應該在 5-50 毫秒之間。這樣可以避免服務器端 Redis 已經掛掉的情況下,客戶端還在死死地等待響應結果。如果服務器端沒有在規定時間內響應,客戶端應該盡快嘗試另外一個 Redis 實例。

客戶端使用當前時間減去開始獲取鎖時間(步驟 1 記錄的時間)就得到獲取鎖使用的時間。當且僅當從大多數(這里是 3 個節點)的 Redis 節點都取到鎖,并且使用的時間小于鎖失效時間時,鎖才算獲取成功。

如果取到了鎖,key 的真正有效時間等于有效時間減去獲取鎖所使用的時間(步驟 3 計算的結果)。

如果因為某些原因,獲取鎖失敗(沒有在至少 N/2+1 個Redis實例取到鎖或者取鎖時間已經超過了有效時間),客戶端應該在所有的 Redis 實例上進行解鎖(即便某些 Redis 實例根本就沒有加鎖成功)。

到這,基本看出來,只要是大多數的 Redis 節點可以正常工作,就可以保證 Redlock 的正常工作。這樣就可以解決前面單點 Redis 的情況下我們討論的節點掛掉,由于異步通信,導致鎖失效的問題。

但是細想后, Redlock 還是存在如下問題:

假設一共有5個Redis節點:A, B, C, D, E。設想發生了如下的事件序列:

  1. 客戶端1 成功鎖住了 A, B, C,獲取鎖成功(但 D 和 E 沒有鎖住)。
  2. 節點 C 崩潰重啟了,但客戶端1在 C 上加的鎖沒有持久化下來,丟失了。
  3. 節點 C 重啟后,客戶端2 鎖住了 C, D, E,獲取鎖成功。
  4. 這樣,客戶端1 和 客戶端2 同時獲得了鎖(針對同一資源)。

哎,還是不能解決故障重啟后帶來的鎖的安全性問題...

針對節點重后引發的鎖失效問題,Redis 作者又提出了 延遲重啟 的概念,大致就是說,一個節點崩潰后,不要立刻重啟他,而是等到一定的時間后再重啟,等待的時間應該大于鎖的過期時間,采用這種方式,就可以保證這個節點在重啟前所參與的鎖都過期,聽上去感覺 延遲重啟 解決了這個問題...

但是,還是有個問題,節點重啟后,在等待的時間內,這個節點對外是不工作的。那么如果大多數節點都掛了,進入了等待,就會導致系統的不可用,因為系統在過期時間內任何鎖都無法加鎖成功。

總之,在 Redis 分布式鎖的實現上還有很多問題等待解決,我們需要認識到這些問題并清楚如何正確實現一個 Redis 分布式鎖,然后在工作中合理的選擇和正確的使用分布式鎖。

但為什么又說,有一部分企業采用Redis來實現分布式鎖呢?其實實現分布式鎖,從中間件上來選,也有 Zookeeper 可選,并且 Zookeeper 可靠性比 Redis 強太多,但是效率是低了點,如果并發量不是特別大,追求可靠性,那么肯定首選 Zookeeper。

關于分布式鎖的實現方案,沒有絕對的好與壞,沒有最好的方案,只有最適合你的業務的方案。

以下兩種方案僅供參考:

  • 如果為了效率,建議使用Redis來實現;
  • 如果追求可靠性,建議使用Zookeeper來實現。

本文轉載自微信公眾號「Java后端技術全?!梗梢酝ㄟ^以下二維碼關注。轉載本文請聯系Java后端技術全棧公眾號。

 

責任編輯:武曉燕 來源: Java后端技術全棧
相關推薦

2019-06-19 15:40:06

分布式鎖RedisJava

2023-01-13 07:39:07

2023-08-21 19:10:34

Redis分布式

2022-01-06 10:58:07

Redis數據分布式鎖

2019-02-26 09:51:52

分布式鎖RedisZookeeper

2022-09-19 08:17:09

Redis分布式

2020-11-16 12:55:41

Redis分布式鎖Zookeeper

2024-10-07 10:07:31

2021-06-16 07:56:21

Redis分布式

2019-07-16 09:22:10

RedisZookeeper分布式鎖

2024-04-01 05:10:00

Redis數據庫分布式鎖

2022-06-16 08:01:24

redis分布式鎖

2021-10-26 19:37:15

RedisRedis應用篇

2021-07-26 11:09:46

Redis分布式技術

2020-07-30 09:35:09

Redis分布式鎖數據庫

2020-07-15 16:50:57

Spring BootRedisJava

2022-07-22 06:55:20

Redis分布式鎖

2023-03-01 08:07:51

2020-07-03 13:29:08

Redis集群哈希槽

2018-07-17 08:14:22

分布式分布式鎖方位
點贊
收藏

51CTO技術棧公眾號

亚洲国产美女搞黄色| 韩日av一区二区| 亚洲欧美国产精品| 国产无遮挡猛进猛出免费软件| 日本免费在线视频| 国产**成人网毛片九色| 欧美综合在线第二页| 亚洲国产精品一区二区久久hs| 亚洲一区二区三区免费| 欧美日韩亚洲一区二区三区| 综合操久久久| 性xxxx视频播放免费| 狠狠久久亚洲欧美| 国产精品福利在线观看| 久久久久久国产精品免费播放| 自拍偷拍一区| 日韩午夜激情视频| 午夜欧美福利视频| 美女在线视频免费| 伊人性伊人情综合网| 亚欧洲精品在线视频免费观看| 亚洲欧美激情在线观看| 蜜桃视频在线一区| 欧美影院在线播放| 国产一级生活片| 色135综合网| 亚洲欧美国产另类| 星空大象在线观看免费播放| 亚洲午夜国产成人| 欧美色图天堂网| 成年人免费在线播放| 久久一卡二卡| 一区二区久久久久久| 亚洲一区bb| 国产一区二区三区福利| 久久影院午夜片一区| 国产精品美女黄网| 性色av蜜臀av| 国产尤物一区二区| 成人写真视频福利网| 亚洲免费视频二区| 久久久久中文| 青青精品视频播放| 久久精品国产成人av| 天天射成人网| 久久久极品av| 亚洲伦理一区二区三区| 91亚洲国产| 精品国产区一区二区三区在线观看| 成人性生交大免费看| 伊人久久亚洲| 精品国产乱子伦一区| 成人一区二区三区仙踪林| 国产一区二区三区亚洲综合 | 久久久久久久久久影院| 亚洲午夜精品一区 二区 三区| 日韩一区av在线| 我要看一级黄色录像| 国产精品久久久久久久久妇女| www.欧美三级电影.com| 在线观看黄网址| 综合天天久久| 欧美激情在线观看| 日韩成人一区二区三区| 亚洲一区激情| 国产99久久久欧美黑人| 亚洲综合精品视频| 国产伦精品一区二区三区在线观看 | 日韩电影中文 亚洲精品乱码 | 成年人免费在线播放| 日本免费一区二区三区四区| 日本道在线观看一区二区| 国产一级做a爰片久久| 久久69成人| 日韩视频在线你懂得| 亚洲激情 欧美| 亚洲69av| xx视频.9999.com| 国产一级二级三级视频| 免费日韩一区二区| 国产免费亚洲高清| 亚洲精品久久久久久动漫器材一区| 成人黄色国产精品网站大全在线免费观看| 国产午夜精品在线| 国产美女视频一区二区三区| 亚洲欧洲成人av每日更新| 中文精品无码中文字幕无码专区| 男人久久天堂| 欧美日韩精品专区| 亚洲精品激情视频| 奇米影视亚洲| 久久久久国产精品www| 在线观看日本网站| 国产一区二区91| 久久精品国产一区二区三区日韩| 午夜视频在线看| 亚洲国产精品久久久男人的天堂| 一级黄色香蕉视频| 亚洲一区二区三区中文字幕在线观看 | 午夜日本精品| 日韩免费黄色av| 成人高潮片免费视频| 久久蜜桃av一区二区天堂| 天天爱天天做天天操| 天堂√8在线中文| 91.麻豆视频| 亚洲一区二区三区蜜桃| 欧美三级视频| 国产免费一区二区三区在线能观看| 亚洲精品.www| 中文字幕一区二区三区视频| 欧美视频第一区| 一区二区三区视频播放| 日韩中文字幕免费看| 三级视频在线观看| 成人三级在线视频| 国产日韩视频在线播放| 日韩精选视频| 日韩电视剧在线观看免费网站| 国产suv一区二区三区| 日本v片在线高清不卡在线观看| 国产欧美日韩视频一区二区三区| 好吊日视频在线观看| 91国偷自产一区二区使用方法| 精品人妻一区二区免费| 性欧美69xoxoxoxo| 国产精品三级美女白浆呻吟| 国产中文在线视频| 狠狠爱在线视频一区| 午夜诱惑痒痒网| 四季av一区二区三区免费观看| 国产成人avxxxxx在线看| 人妻少妇精品无码专区久久| 亚洲精品国产无天堂网2021| 国产成人在线综合| 日韩伦理视频| 国产精品日日做人人爱| av在线电影网| 欧美视频一区二区在线观看| 国产手机在线观看| 久久亚洲一区| 欧美二区在线看| 亚洲一二三四| 亚洲男人7777| 波多野结衣啪啪| 国产三级精品在线| 欧美黑人又粗又大又爽免费| 蜜桃视频欧美| 欧美日韩亚洲精品内裤| 国产精品久久久久久久久久久久久久 | 91精品在线观看入口| 免费一级特黄3大片视频| 日韩精品成人一区二区三区| 日韩一区不卡| 成人av色网站| 色婷婷av一区二区三区在线观看 | 最近在线中文字幕| 亚洲人在线观看| 你懂的国产在线| 久久久高清一区二区三区| 国产精品久久久久9999小说| 成人羞羞动漫| 3d动漫啪啪精品一区二区免费| 超碰个人在线| 精品久久久久一区| 日韩 欧美 亚洲| 91年精品国产| 国产成人精品视频ⅴa片软件竹菊| 精品国产a一区二区三区v免费| 国产精品视频久久久| 国产精品久久麻豆| 精品久久久三级丝袜| 久久中文字幕免费| 中文字幕va一区二区三区| 色呦色呦色精品| 国产精品videosex极品| 精品无码久久久久久久动漫| 欧洲av一区二区| www.亚洲一区| 欧性猛交ⅹxxx乱大交| 日本高清不卡aⅴ免费网站| 免费精品在线视频| 暴力调教一区二区三区| 亚洲欧美另类动漫| 欧美精品福利| 日本一区网站| 中文字幕久久精品一区二区| 国产91精品青草社区| 91亚洲免费视频| 精品日产乱码久久久久久仙踪林| 日韩美女视频免费看| 黄a在线观看| 精品中文字幕久久久久久| 伊人色综合久久久| 亚洲成人高清在线| 日韩视频在线观看免费视频| 国产成人综合网站| 国产最新免费视频| 国产精品99在线观看| 精品一区2区三区| 高清久久一区| 欧日韩在线观看| 一区二区三区伦理| 尤物99国产成人精品视频| 国产99久久九九精品无码免费| 日韩欧美黄色动漫| 麻豆疯狂做受xxxx高潮视频| 国产午夜一区二区三区| 国产白袜脚足j棉袜在线观看| 日本午夜精品视频在线观看 | 久久国产主播| 国产精品久久国产| 成人av二区| 麻豆91蜜桃| youjizzjizz亚洲| 国产主播喷水一区二区| 电影久久久久久| 97精品久久久中文字幕免费| 黄网站在线免费看| 一区二区三区在线播放欧美| 亚洲日本在线播放| 欧美大胆一级视频| 国产精品欧美亚洲| 欧美在线一二三| 久久久久99精品成人片三人毛片| 一区二区在线观看免费| 三级全黄做爰视频| 国产精品每日更新| 人与嘼交av免费| 久久久久国产精品厨房| 精品无码国产一区二区三区51安| 国产成人午夜99999| 欧美精品色视频| 激情综合网激情| 狠狠干狠狠操视频| 蜜桃视频第一区免费观看| av免费网站观看| 久久久久国产精品一区二区| 国产无套内射久久久国产| 国产日韩专区| 两根大肉大捧一进一出好爽视频| 99香蕉国产精品偷在线观看| 国产aaa免费视频| 好吊一区二区三区| www.av蜜桃| 亚洲国产裸拍裸体视频在线观看乱了中文 | 麻豆影视在线观看_| 中文字幕在线看视频国产欧美| 久草在线网址| 亚洲视频视频在线| 国产三级视频在线| 中文字幕一区电影| 黄色在线免费网站| 欧美成人性色生活仑片| 污污的视频在线观看| 欧美极品少妇与黑人| xxx.xxx欧美| 4438全国成人免费| 日韩精品99| 国产女精品视频网站免费| 电影91久久久| 国产精品成人一区二区三区 | 日韩成人激情视频| 可以免费看污视频的网站在线| 国产亚洲精品综合一区91| 91精彩在线视频| 久久视频在线观看免费| 青春草免费在线视频| 96精品视频在线| 素人啪啪色综合| 91久久精品www人人做人人爽| a看欧美黄色女同性恋| 美日韩精品免费| 久久密一区二区三区| 激情五月六月婷婷| 国产一区二区高清| 自拍偷拍21p| 国产成人精品影视| 伊人网伊人影院| 中文字幕在线视频一区| 国产一级在线播放| 色噜噜狠狠成人网p站| 91国在线视频| 精品成人在线观看| yiren22亚洲综合伊人22| 免费91在线视频| 偷拍自拍在线看| 成人欧美在线观看| 羞羞色国产精品网站| 中文字幕一区二区中文字幕 | 91精品国产高清久久久久久91| 国产伦精品一区二区三区视频金莲| 国产精品嫩草视频| a级日韩大片| 亚洲国产成人不卡| 亚洲激情综合| 三上悠亚在线一区| 91在线丨porny丨国产| 国产福利视频网站| 色悠悠久久综合| 国产激情视频在线播放| 亚洲色图15p| 黄页网站在线| 国产一区香蕉久久| 亚洲区小说区图片区qvod按摩| 青春草在线视频免费观看| 免费在线成人| 日韩高清一二三区| 国产精品剧情在线亚洲| 日韩美女一级片| 666欧美在线视频| 国产三级电影在线| 69av在线视频| 中文字幕亚洲在线观看| 亚洲国产高清国产精品| 久久久久久久尹人综合网亚洲| 麻豆传媒在线看| 国产精品伦理一区二区| 一级成人黄色片| 精品国产伦一区二区三区观看方式| 色网站免费在线观看| 国产成人97精品免费看片| 精品中国亚洲| 成人小视频在线观看免费| 韩日欧美一区二区三区| 秋霞网一区二区三区| 日韩人体视频一二区| 日本黄色大片视频| 欧美国产日韩精品| 久久丁香四色| 宅男一区二区三区| 免费看欧美美女黄的网站| 爱爱免费小视频| 欧美日韩另类字幕中文| 污视频在线免费观看| 久久久久久有精品国产| 精品成人av一区二区三区| 一区二区三区视频在线看| av免费观看在线| 久久精品国产一区二区三区| 欧美爱爱视频| 一本久久a久久精品vr综合| 日本网站在线观看一区二区三区 | 亚洲国产精品suv| 久久影院中文字幕| 国产欧美视频在线| 影音先锋成人资源网站| 国产精一区二区三区| 波多野结衣亚洲色图| 日韩欧美一区二区在线视频| 成人免费网址| 91久久伊人青青碰碰婷婷| 欧美在线免费| 少妇伦子伦精品无吗| 亚洲午夜日本在线观看| 人成网站在线观看| 91成人天堂久久成人| 亚洲欧美校园春色| 校园春色 亚洲色图| 亚洲欧洲日韩在线| 亚洲AV无码成人片在线观看| 九九精品视频在线| 欧美freesex8一10精品| 日韩网址在线观看| 亚洲国产高清在线观看视频| 伊人网中文字幕| 欧美成人免费全部观看天天性色| 白嫩白嫩国产精品| 成人三级视频在线播放| 中文字幕+乱码+中文字幕一区| 97国产成人无码精品久久久| 久久99久久亚洲国产| 三级小说欧洲区亚洲区| 免费看国产黄色片| 一区二区欧美精品| 免费理论片在线观看播放老| 国产精品一区二区三区在线播放| 中文字幕免费一区二区三区| 国产a级黄色片| 欧美综合色免费| av大大超碰在线| 久久综合入口| 开心九九激情九九欧美日韩精美视频电影 | 久久91麻豆精品一区| 天天综合网日韩| 亚洲国产欧美日韩另类综合| 国产高清自拍视频在线观看| 亚洲影视中文字幕| 午夜一区二区三区不卡视频| 青青青视频在线免费观看| 精品国产露脸精彩对白 | 懂色一区二区三区av片| 久久av在线| 欧美日韩精品一区二区三区视频播放 | 久香视频在线观看| 国产日韩在线免费| 亚洲国内欧美| 91麻豆精品久久毛片一级| 亚洲娇小xxxx欧美娇小| 亚洲伦理网站|