Redis实现分布式锁

Redis实现分布式锁

Redis是一个开放源代码(BSD许可)的内存中数据结构存储,用作数据库,缓存和消息代理。它支持数据结构,例如字符串,哈希,列表,集合,带范围查询的排序集合,位图,超级日志,带有半径查询和流的地理空间索引。Redis具有内置的复制,Lua脚本,LRU驱逐,事务和不同级别的磁盘持久性,并通过Redis Sentinel和Redis Cluster自动分区提供了高可用性。

一、分布式锁的实现

分布式锁一般有三种实现方式:1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁。

可靠性

首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

  1. 互斥性。在任意时刻,只有一个客户端能持有锁。
  2. 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
  3. 具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
  4. 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
1、导入redis依赖
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、在Spring boot配置文件中加入配置
spring:
  redis:
    host: 192.168.34.100 #redis地址
    port: 6379
    database: 0
    lettuce:
      pool:
        max-active: 8 #最大连接数,负数表示无限制
        max-wait: 10000 #最大等待ms,负数表示无限制
        max-idle: 8 #最大空闲连接数,负数表示无限制
        min-idle: 0 #最小空闲连接数
      shutdown-timeout: 100 #shutdown超时

如需解决乱码问题需要在启动类上加入,全局配置

	@Autowired
    private RedisTemplate redisTemplate;

    @Bean
    public RedisTemplate<String, Object> stringSerializerRedisTemplate() {
        RedisSerializer<String> stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        redisTemplate.setHashValueSerializer(stringSerializer);
        return redisTemplate;
    }
3、编码(加锁、解锁、锁超时)

要实现加锁可以根据redis中的setnx命令实现,该命令可以确保只有一个key。

 @Autowired
 private RedisTemplate redisTemplate;

//线程唯一id
String uuid = UUID.randomUUID().toString();

/**
 * 加锁,setIfAbsent=setnx命令
 * 如果redis已经有该key后返回false,加锁失败
 */
Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(String.valueOf(orderRequest.getSchedulerId()), uuid);
/**
*如果加锁失败后进入循环等待
*/
while(!aBoolean){
    TimeUnit.SECONDS.sleep(1);
}

uuid : 这里会随机生成一个id,为确保加锁和解锁的一致性,一个线程加锁后必须有他解锁,解锁时利用id做判断。解决误删锁。

锁超时:如果一个得到锁的线程在执行任务的过程中挂掉,来不及显式地释放锁,该资源将会被永远占用,其他线程将无法访问

  • 可以给锁添加失效时间(expire方法)
redisTemplate.expire(String.valueOf(scheduleId).concat(":").concat(seatArrays[i]),20, TimeUnit.SECONDS);

加锁setnx和锁超时expire两个命令未非原子性操作,当执行加锁setnx后,若因网络或客户端问题锁超时expire命令未成功执行时,锁将无法被释放

因此需要调用setIfAbsent()可以同时设置的失效时间,从而解决程序出错二导致的无法释放锁的死锁问题。

redisTemplate.opsForValue().setIfAbsent(String.valueOf(orderRequest.getSchedulerId()),uuid,10,TimeUnit.SECONDS);

解锁使用del命令,删除掉锁。

/**
*判断uuid和锁的值是否一致,
*/
if(uuid.equals(redisTemplate.opsForValue().get(String.valueOf(orderRequest.getSchedulerId())))){
    //释放锁
    redisTemplate.delete(String.valueOf(orderRequest.getSchedulerId()));
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值