Redisson-分布式鎖單Redis節點模式
為什么要用分布式鎖?
使用分布式鎖的主要目的是為了解決多線程或多進程并發訪問共享資源時可能出現的競爭條件和數據一致性問題。舉一些實際場?:
- 數據庫并發控制:在分布式系統中,多個節點同時操作數據庫時,可能會導致數據不一致或沖突。通過使用分布式鎖,可以確保同一時間只有一個節點能夠對特定數據進行修改,從而避免出現臟讀、幻讀等問題。
- 資源限制和瓶頸控制:某些資源(如文件、接口調用次數等)需要限制同時訪問的數量,在高并發環境下通過使用分布式鎖可以有效地控制資源的訪問數量。
- 防止死鎖:在復雜系統中,由于各種原因(如網絡故障、程序錯誤等),可能導致死鎖情況。通過使用帶有超時機制的分布式鎖,可以防止因為單個節點故障而導致整個系統陷入死鎖狀態。
使用分布式鎖能夠保證共享資源被安全地訪問和修改,并且能夠提供良好的并發性能和可靠性。
目前的市場使用的分布式鎖有哪些?(個人了解不代表所有)
-
基于數據庫的分布式鎖
優點:簡單易實現,使用現有的數據庫即可,不需要額外的基礎設施。
缺點:性能相對較低,數據庫的I/O操作開銷大,還需要處理數據庫的死鎖問題。 -
基于Redis的分布式鎖
優點:高性能,Redis是內存數據庫,讀寫速度快,Redis支持多種數據結構,靈活性高。
缺點:需要確保Redis集群的高可用性和一致性,否則可能導致鎖的失效。 -
基于Redisson的分布式鎖
優點: 內置多種鎖的實現,適用于不同場景,提高了鎖的可靠性。
缺點:封裝了很多功能,引入了一些額外的開銷,相對直接使用Redis的命令,增加了項目依賴。 -
基于Zookeeper的分布式鎖
優點:實現分布式鎖的過程中能夠自動處理網絡分區和節點故障,支持臨時節點,可以自動釋放鎖。
缺點:實現較為復雜!!!性能相對Redis較低,因為Zookeeper需要維護強一致性。
使用Redisson實現分布式鎖
我用的是Spring Boot 的框架,沒有什么心思寫文章就直接寫代碼吧~
demo包類結構
pom.xml
<dependencies><!-- redis緩存 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Redisson分布式鎖 --><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.12.0</version></dependency></dependencies>
yml配置
server:port: 8081servlet:context-path: /api
spring:redis:host: 服務器ipport: 6379
# password: 123456timeout: 60000database: 5
配置類
package com.springboot.redisson.config;import lombok.Data;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;@Data
@Configuration
@ConfigurationProperties("spring.redis")
public class RedissonConfig {private String host;private String password;private String port;private int timeout = 3000;private int connectionPoolSize = 64;private int connectionMinimumIdleSize=10;private int pingConnectionInterval = 60000;private static String ADDRESS_PREFIX = "redis://";/*** 自動裝配**/@BeanRedissonClient redissonSingle() {Config config = new Config();// 判斷redis 的host是否為空if(StringUtils.isEmpty(host)){throw new RuntimeException("host is empty");}// 配置host,port等參數SingleServerConfig serverConfig = config.useSingleServer()// 節點地址.setAddress(ADDRESS_PREFIX + this.host + ":" + port)// 命令等待超時,單位:毫秒.setTimeout(this.timeout).setPingConnectionInterval(pingConnectionInterval)// 連接池大小.setConnectionPoolSize(this.connectionPoolSize)// 最小空閑連接數.setConnectionMinimumIdleSize(this.connectionMinimumIdleSize);// 判斷進入redis 是否密碼if(!StringUtils.isEmpty(this.password)) {serverConfig.setPassword(this.password);}return Redisson.create(config);}
}
controller
package com.springboot.redisson.controller;import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RCountDownLatch;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.UUID;
import java.util.concurrent.TimeUnit;@Slf4j
@RestController
@RequestMapping("redisson")
public class RedissonController {@ResourceRedissonClient redissonSingle;@ResourceRedisTemplate<String,Object> redisTemplate;@GetMapping("/")private String TestString(){return "Hello redisson!";}/*** RLock lock = redissonSingle.getLock("my-redisson-lock");* lock.lock();* 使用的是可重入鎖* 可重入鎖: 可重入鎖是一個分布式鎖,它可以用于多個線程訪問同一把鎖的等待保證數據的唯一性* 例如: 當有一個線程在訪問一個可重入鎖的時候,就會自動加鎖,其它線程再次訪問同一把鎖有時候只能等待,* 當線程釋放鎖的時候,其它線程才可以訪問,每次只能有一個線程進行訪問,* @return*/@GetMapping("no1")public String TestRedisson01(){RLock lock = redissonSingle.getLock("my-redisson-lock");lock.lock();try {log.info("NO1加鎖成功,正在處理業務邏輯》》》》》");System.out.println("好長的業務");Thread.sleep(50000);} catch (InterruptedException e) {throw new RuntimeException(e);}finally {lock.unlock();log.info("NO1解鎖成功");}return "NO1線程已經走完成!";}@GetMapping("no2")public String TestRedisson02(){RLock lock = redissonSingle.getLock("my-redisson-lock");lock.lock();try {log.info("NO2加鎖成功,正在處理業務邏輯》》》》》");System.out.println("好長的業務");Thread.sleep(50000);} catch (InterruptedException e) {throw new RuntimeException(e);}finally {lock.unlock();log.info("NO2解鎖成功");}return "NO2線程已經走完成!";}/*** 讀寫鎖* RReadWriteLock readWriteLock = redissonSingle.getReadWriteLock("my-lock");* RLock rLock = readWriteLock.readLock();* rLock.lock();* rLock.unlock();* 讀寫鎖: 讀寫鎖是一個分布式鎖,它允許多個線程同時讀,但是只允許一個線程寫,寫操作的時候,讀操作會被阻塞,*/@GetMapping("read")public String TestReadLock(){RReadWriteLock readWriteLock = redissonSingle.getReadWriteLock("my-lock");RLock rLock = readWriteLock.readLock();rLock.lock();String s = "";try {log.info("讀寫鎖操作中,正在處理業務邏輯》》》》》");s = UUID.randomUUID().toString();redisTemplate.opsForValue().set("redisson:readWriteLock",s,200, TimeUnit.SECONDS);Thread.sleep(5000);}catch (Exception e){e.printStackTrace();}finally {rLock.unlock();}return s;}@GetMapping("writel")public String TestWriteLock(){RReadWriteLock readWriteLock = redissonSingle.getReadWriteLock("my-lock");RLock rLock = readWriteLock.writeLock();rLock.lock();String s = "";try {log.info("讀鎖操作中,正在處理業務邏輯》》》》》");String key = (String) redisTemplate.opsForValue().get("redisson:readWriteLock");if (!StringUtils.isEmpty(key)){s = key;}}catch (Exception e){e.printStackTrace();}finally {rLock.unlock();}return s;}/*** 閉鎖* RCountDownLatch door = redissonSingle.getCountDownLatch("door");* door.trySetCount(5);* door.await();** door.countDown();* 閉鎖: 閉鎖是一個分布式閉鎖,它允許一個或多個線程等待,直到其他線程完成一系列操作,然后才繼續執行。*/@GetMapping("/lockDoor")public String lockDoor(){try {RCountDownLatch door = redissonSingle.getCountDownLatch("door");door.trySetCount(5);door.await();} catch (InterruptedException e) {throw new RuntimeException(e);}return "放假了。。。。。";}@GetMapping("/gogo/{id}")public String gogo(@PathVariable("id")String id){RCountDownLatch door = redissonSingle.getCountDownLatch("door");door.countDown();return id+"班的人都走了。。。。。";}@GetMapping("/modify")public String modifyData(){RLock lock = redissonSingle.getLock("my_modify_locl");if (!lock.tryLock()){return "修改失敗,當前信息正在被人修改";}try {System.out.println("運行一個超長代碼中");Thread.sleep(50000);return "修改成功";}catch (Exception e){e.printStackTrace();}finally {lock.unlock();}return "最后的代碼";}
}