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: 8081
servlet:
context-path: /api
spring:
redis:
host: 服务器ip
port: 6379
# password: 123456
timeout: 60000
database: 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://";
/**
* 自动装配
*
*/
@Bean
RedissonClient 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 {
@Resource
RedissonClient redissonSingle;
@Resource
RedisTemplate<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 "最后的代码";
}
}