Redis实现分布式锁

一、使用Redis中的SETNX指令实现分布式锁

命令SET resource-name anystring NX EX max-lock-time是一种用 Redis 来实现锁机制的简单方法

  • EX seconds – 过期时间,单位秒
  • PX milliseconds – 过期时间,单位分钟
  • NX – 只有key不存在才设置key

设计思路:根据SET resource-name anystring NX EX max-lock-time设置分布式锁,如果上述命令返回OK,那么就可以获得锁(如果返回Nil,那么在一段时间之后重新尝试),从数据库中获取数据操作成功后,通过DEL命令来释放锁。简单实现代码如下:

    Boolean isSet = redisTemplate.opsForValue().setIfAbsent("lock",uuid);
    if(isSet){
   
        dataFromDb = getDataFromDb();
        redisTemplate.delete("lock");//删除锁
	}else{
   
		//等待一段时间后重试上述操作,即自旋锁操作
	}
}

上述代码会出现三个问题:

  • 问题一:执行getDataFromDb()出现异常,那么后续的删除锁操作将无法执行,所以需要将redisTemplate.delete("lock")使用try-catch-finally将异常抛出,并在finally中执行删除锁操作
    try{
         
    	dataFromDb = getDataFromDb();
    }finally {
         
      	redisTemplate.delete("lock");//删除锁
    }
    
  • 问题二:如果执行完setIfAbsent()即加锁完成后该进程中断导致后面代码不再执行,那么该锁将永远不会被删除,可以给锁设置一个过期时间避免这种情况。注意加锁设置过期时间必须同时进行,即必须保证这两步是一个原子操作,不然当加完锁进程中断但过期时间还没设置时还是会出现问题二。加锁设置过期时间在Spring可以通过setIfAbsent()同时传入锁名和过期时间这两个参数实现。
    //加锁的同时设置过期时间,都设在setIfAbsent中可以保证原子性
    Boolean isSet = redisTemplate.opsForValue().setIfAbsent("lock",uuid,300,TimeUnit.SECONDS);
    
  • 问题三:设置了过期时间后,如果获得锁后当前进程还没执行完时过期时间正好到了导致当前进程的锁没了,其他进程占用锁,这样当前进程执行到删除锁步骤时会把其他进程占用的锁给删了。
    1) 在删除锁前可以通过value判断锁是不是当前线程的。但是获取value删除锁两个操作中间如果有时间间隔还是会有可能出现问题三的情况,所以必须保证这两步的原子性。这两步的原子性可以通过lua脚本来实现。
    2) 不过上述方法1)解决的是当前线程删除其他线程的锁的情况,没有从根本上解决问题,根本问题是当前线程还在执行,但是执行时锁由于过期被删,其他线程占用锁,这本身就很不合理
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值