亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java實現(xiàn)redis分布式鎖的三種方式

 更新時間:2022年08月03日 09:18:58   作者:Ajekseg  
本文主要介紹了Java實現(xiàn)redis分布式鎖的三種方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

一、引入原因

在分布式服務(wù)中,常常有如定時任務(wù)、庫存更新這樣的場景。

在定時任務(wù)中,如果不使用quartz這樣的分布式定時工具,只是簡單的使用定時器來進(jìn)行定時任務(wù),在服務(wù)分布式部署中,就有可能存在定時任務(wù)并發(fā)執(zhí)行,造成一些問題。

在庫存更新這樣的場景中,我們服務(wù)對數(shù)據(jù)庫同一條記錄進(jìn)行更新,并記錄。對記錄更新可以使用分布式鎖,但對操作進(jìn)行記錄時,可能造成讀未提交,造成記錄錯亂的情況。

在以上的場景中,我們引入了分布式事務(wù)鎖。

二、分布式鎖實現(xiàn)過程中的問題

問題一:異常導(dǎo)致鎖沒有釋放

這個問題形成的原因就是程序在獲取到鎖之后,執(zhí)行業(yè)務(wù)的過程中出現(xiàn)了異常,導(dǎo)致鎖沒有被釋放。通俗的話說:上廁所的人死在了廁所里面,導(dǎo)致“坑位”資源死鎖無法被釋放。(當(dāng)然這種情況出現(xiàn)的概率很小,但概率小不等于不存在。)

解決方案: 為redis的key設(shè)置過期時間,程序異常導(dǎo)致的死鎖,在到達(dá)過期時間之后鎖自動釋放。也就說廁所門是電子鎖,鎖定的最長時間是有限制的,超過時長鎖就會自動打開釋放"坑位"資源。

image-20220428112311092

問題二:獲取鎖與設(shè)置過期時間操作不是原子性的

上文中我們雖然獲取到鎖,也設(shè)置了過期時間,看似完美。但是在高并發(fā)的場景下仍然會出問題,因為“獲取鎖”與“設(shè)置過期時間”是兩個redis操作,兩個redis操作不是原子性的。
可能出現(xiàn)這種情況:就在獲取鎖之后,設(shè)置過期時間之前程序宕機了。鎖被獲取到了但沒有設(shè)置過期時間,最后又成為死鎖。

解決方案: 獲取鎖的同時設(shè)置過期時間

image-20220428112803792

問題三:鎖過期之后被別的線程重新獲取與釋放

這個問題出現(xiàn)的場景是:假如某個應(yīng)用集群化部署存在多個進(jìn)程實例,實例A、實例B。實例A獲取到鎖,但是執(zhí)行過程超時了(數(shù)據(jù)庫層面或其他層面導(dǎo)致操作執(zhí)行超時)。超時之后鎖被自動釋放了,實例B獲取到鎖,并執(zhí)行業(yè)務(wù)程序,執(zhí)行完成之后把鎖刪除了。

解決方案: 在釋放鎖之前判斷一下,這把鎖是不是自己的那一把,如果是別人的鎖你就不要動。怎么判斷這把鎖是不是自己的?加鎖時為value賦隨機值,加鎖的隨機值等于解鎖時的獲取到的值,才能證明這把鎖是你的。

問題四:鎖的釋放不是原子性的

大家仔細(xì)看代碼,鎖的釋放時三個操作,這三個操作不是原子性的。也就是說在高并發(fā)的場景下,你剛get到的redis key有可能也被別的線程get了,你剛要刪除別的線程可能已經(jīng)把這個key刪除了。

解決方案: 我們可以使用redis lua腳本(lua腳本是在一個事務(wù)里面執(zhí)行的,可以保證原子性)。在Java代碼中可以以字符串的形式存在。如下:

String script = 
	"if redis.call('get', KEYS[1]) == ARGV[1] 
		then return redis.call('del', KEYS[1]) 
	else 
		return 0 
	end";

問題五:其他的問題?

上面我們分析了很多使用redis實現(xiàn)分布式鎖可能出現(xiàn)的問題及解決方案,其實在實際的開發(fā)應(yīng)用中還會有更多的問題。比如:

  • 目前我們的程序獲取不到鎖,就無限的重試,是不是應(yīng)該在重試一定的次數(shù)之后就拋出異常?在有限的時間內(nèi)通過異常給用戶一個友好的響應(yīng)。比如:程序太忙,請您稍后再試!
  • 程序A沒有執(zhí)行完成,鎖定的key就過期了。雖然過期之后會自動釋放鎖,但是我的程序A的確沒有執(zhí)行完成啊,也沒有異常拋出,就是執(zhí)行的時間比較長,這個時候是不是應(yīng)該對鎖定的key進(jìn)行續(xù)期?

這些問題在高并發(fā)場景下會出現(xiàn),實際上分布式鎖的細(xì)節(jié)實踐有很多的現(xiàn)成的解決方案,不用我們?nèi)プ约簩崿F(xiàn)。比較完整優(yōu)秀的分布式鎖實現(xiàn)包括:

RedisLockRegistry是spring-integration-redis中提供redis分布式鎖實現(xiàn)類

基于Redisson實現(xiàn)分布式鎖原理(Redission是一個獨立的redis客戶端,是與Jedis、Lettuce同級別的存在)

三、具體實現(xiàn)

1. RedisTemplate

RedisTemplate<String, String> redisTemplate;

public void updateUserWithRedisLock(SysUser sysUser) throws InterruptedException {
  // 占分布式鎖,去redis占坑
  // 1. 分布式鎖占坑
Boolean lock = redisTemplate.opsForValue().setIfAbsent("SysUserLock" + sysUser.getId(), "value", 30, TimeUnit.SECONDS);
  if(lock) {
    //加鎖成功... 
		// todo business
    
    
    redisTemplate.delete("SysUserLock" + sysUser.getId());   //刪除key,釋放鎖
  } else {
    Thread.sleep(100);   // 加鎖失敗,重試
    updateUserWithRedisLock(sysUser);
  }
}

setIfAbsent方法的作用是在某一個lock key不存在的時候,才能返回true;如果這個key已經(jīng)存在了就返回false,返回false就是獲取鎖失敗。setIfAbsent函數(shù)功能類似于redis命令行setnx。

2. RedisLockRegistry

集成spring-integration-redis

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
     <groupId>org.springframework.integration</groupId>
     <artifactId>spring-integration-redis</artifactId>
</dependency>

注冊RedisLockRegistry

@Configuration
public class RedisLockConfig {

     @Bean
     public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
         //第一個參數(shù)redisConnectionFactory
         //第二個參數(shù)registryKey,分布式鎖前綴,設(shè)置為項目名稱會好些
         //該構(gòu)造方法對應(yīng)的分布式鎖,默認(rèn)有效期是60秒.可以自定義
         return new RedisLockRegistry(redisConnectionFactory, "boot-launch");
         //return new RedisLockRegistry(redisConnectionFactory, "boot-launch",60);
     }
}

使用RedisLockRegistry

代碼中實現(xiàn)

@Resource
private RedisLockRegistry redisLockRegistry;

public void updateUser(String userId) {
String lockKey = “config” + userId;
Lock lock = redisLockRegistry.obtain(lockKey); //獲取鎖資源
try {
lock.lock(); //加鎖

//這里寫需要處理業(yè)務(wù)的業(yè)務(wù)代碼
} finally {
lock.unlock(); //釋放鎖
}
}

注解實現(xiàn)

@RedisLock("lock-key")
public void save(){
}

3. 使用redisson實現(xiàn)分布式鎖

集成redisson

<dependency>
  <groupId>org.redisson</groupId>
  <artifactId>redisson-spring-boot-starter</artifactId>
  <version>3.15.0</version>
  <exclusions>
    <exclusion>
      <groupId>org.redisson</groupId>
      <!-- 默認(rèn)是 Spring Data Redis v.2.3.x ,所以排除掉-->
      <artifactId>redisson-spring-data-23</artifactId>
    </exclusion>
  </exclusions>
</dependency>

配置

在配置文件中加

spring:
  redis:
    redisson:
      file: classpath:redisson.yaml

然后新建一個redisson.yaml文件,也放在resouce目錄下

singleServerConfig:
  idleConnectionTimeout: 10000
  connectTimeout: 10000
  timeout: 3000
  retryAttempts: 3
  retryInterval: 1500
  password: 123456
  subscriptionsPerConnection: 5
  clientName: null
  address: "redis://192.168.161.3:6379"
  subscriptionConnectionMinimumIdleSize: 1
  subscriptionConnectionPoolSize: 50
  connectionMinimumIdleSize: 32
  connectionPoolSize: 64
  database: 0
  dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
transportMode: "NIO"

實現(xiàn)

@Resource
private RedissonClient redissonClient;

public void updateUser(String userId) {
  String lockKey = "config" + userId;
  RLock lock = redissonClient.getLock(lockKey);  //獲取鎖資源
  try {
    lock.lock(10, TimeUnit.SECONDS);   //加鎖,可以指定鎖定時間

    //這里寫需要處理業(yè)務(wù)的業(yè)務(wù)代碼
  } finally {
    lock.unlock();   //釋放鎖
  }
}
  • 相對于RedisLockRegistry另一個小優(yōu)點是:我們可以為每一個鎖指定鎖定的超時時間。RedisLockRegistry目前只能針對所有的鎖設(shè)定統(tǒng)一的超時時間
  • 如果業(yè)務(wù)執(zhí)行超時之后,再去unlock會拋出java.lang.IllegalMonitorStateException

到此這篇關(guān)于Java實現(xiàn)redis分布式鎖的三種方式的文章就介紹到這了,更多相關(guān)Java redis分布式鎖 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Drools Fusion(CEP)定義及使用方法講解

    Drools Fusion(CEP)定義及使用方法講解

    今天小編就為大家分享一篇關(guān)于Drools Fusion(CEP)定義及使用方法講解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • 淺談Spring6中的反射機制

    淺談Spring6中的反射機制

    Java反射機制是Java語言中一種動態(tài)(運行時)訪問、檢測、修改它本身的能力,主要作用是動態(tài)(運行時)獲取類的完整結(jié)構(gòu)信息、調(diào)用對象的方法,需要的朋友可以參考下
    2023-05-05
  • 關(guān)于ElasticSearch的常用增刪改查DSL和代碼

    關(guān)于ElasticSearch的常用增刪改查DSL和代碼

    這篇文章主要介紹了關(guān)于ElasticSearch的常用增刪改查DSL和代碼,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • 深入了解Java I/O 之File類

    深入了解Java I/O 之File類

    這篇文章主要介紹了Java I/O深入學(xué)習(xí)之File和RandomAccessFile, I/O系統(tǒng)即輸入/輸出系統(tǒng),對于一門程序語言來說,創(chuàng)建一個好的輸入/輸出系統(tǒng)并非易事。需要的朋友可以參考下
    2021-08-08
  • Java畢業(yè)設(shè)計實戰(zhàn)之二手書商城系統(tǒng)的實現(xiàn)

    Java畢業(yè)設(shè)計實戰(zhàn)之二手書商城系統(tǒng)的實現(xiàn)

    這是一個使用了java+JSP+Springboot+maven+mysql+ThymeLeaf+FTP開發(fā)的二手書商城系統(tǒng),是一個畢業(yè)設(shè)計的實戰(zhàn)練習(xí),具有在線書城該有的所有功能,感興趣的朋友快來看看吧
    2022-01-01
  • SpringBoot整合EasyExcel的完整過程記錄

    SpringBoot整合EasyExcel的完整過程記錄

    easyexcel是阿里巴巴旗下開源項目,主要用于Excel文件的導(dǎo)入和導(dǎo)出處理,下面這篇文章主要給大家介紹了關(guān)于SpringBoot整合EasyExcel的完整過程,需要的朋友可以參考下
    2021-12-12
  • 超細(xì)致講解Spring框架 JdbcTemplate的使用

    超細(xì)致講解Spring框架 JdbcTemplate的使用

    在之前的Javaweb學(xué)習(xí)中,學(xué)習(xí)了手動封裝JdbcTemplate,其好處是通過(sql語句+參數(shù))模板化了編程。而真正的JdbcTemplate類,是Spring框架為我們寫好的。它是 Spring 框架中提供的一個對象,是對原始 Jdbc API 對象的簡單封裝。
    2021-09-09
  • SpringBoot中使用Redis的完整實例

    SpringBoot中使用Redis的完整實例

    這篇文章主要給大家介紹了關(guān)于SpringBoot中使用Redis的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Mybatis-Plus實現(xiàn)公共字段自動填充的項目實踐

    Mybatis-Plus實現(xiàn)公共字段自動填充的項目實踐

    本文主要介紹了Mybatis-Plus實現(xiàn)公共字段自動填充的項目實踐,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • SpringBoot默認(rèn)使用HikariDataSource數(shù)據(jù)源方式

    SpringBoot默認(rèn)使用HikariDataSource數(shù)據(jù)源方式

    這篇文章主要介紹了SpringBoot默認(rèn)使用HikariDataSource數(shù)據(jù)源方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10

最新評論