当前位置:   article > 正文

redisson分布式锁与spring注解事务共用产生的一系列问题,及对应解决方案_redis分布式锁和spring事务

redis分布式锁和spring事务

前言

有锁才有自由,生活中不存在绝对的自由,绝对的自由通常对应的无序和混沌,只有在道德、法律、伦理的约束下的相对自由,才能使人感受到自由。

而在多线程编程中,锁是至关重要的,锁就是道德,就是法律约束,没有锁的多线程环境将会是混乱的,所有线程都在争夺资源,最后的结果就是导致系统崩溃,而有了锁之后,多线程环境才能稳定高效的工作。

事故现场

某些原因,获取锁失败-------没有在至少N/2+1个Redis实例取到锁,或者取锁时间已经超过了有效时间),客户端应该在所有的Redis实例上进行解锁(即便某些Redis实例根本就没有加锁成功,防止某些节点获取到锁,但是客户端没有得到响应,而导致接下来的一段时间不能被重新获取锁。

解决方案

redisson已经有对redlock算法封装,只需要拿来小心使用即可。

POM依赖

  1. <dependency>
  2.     <groupId>org.redisson</groupId>
  3.     <artifactId>redisson</artifactId>
  4.     <version>3.3.2</version>
  5. </dependency>

redission封装的redlock算法实现的分布式锁用法,非常简单,跟重入锁(ReentrantLock)有点类似,使用的RedLock做分布式锁管理,用spring注解事务管理,具体使用:

  1. Config config = new Config();
  2. config.useSentinelServers().addSentinelAddress("127.0.0.1:6369","127.0.0.1:6379", "127.0.0.1:6389")
  3. .setMasterName("masterName")
  4. .setPassword("password").setDatabase(0);
  5. RedissonClient redissonClient = Redisson.create(config);
  6. // 还可以getFairLock(), getReadWriteLock()
  7. RLock redLock = redissonClient.getLock("REDLOCK_KEY");
  8. boolean isLock;
  9. try {
  10. isLock = redLock.tryLock();
  11. // 500ms拿不到锁, 就认为获取锁失败。10000ms即10s是锁失效时间。
  12. isLock = redLock.tryLock(500, 10000, TimeUnit.MILLISECONDS);
  13. if (isLock) {
  14. //TODO if get lock success, do something;
  15. }
  16. } catch (Exception e) {
  17. } finally {
  18. // 无论如何, 最后都要解锁
  19. redLock.unlock();
  20. }

但是在实现过程中,遇到如下两个映像深刻的问题:
1、分布式锁与spring注解事务共用产生的问题
2、锁在事务提交前超时问题

使用分布式锁RedLock及spring事务实现

  1. public markScenicSpot(){
  2.   //设置锁为destId
  3.    RLock lock = redisson.getLock("Afanti_markScenicSpot_updateCountwantAndCountbeenLock_" + ID);
  4.    //尝试获取锁
  5.    long lockTimeOut = 30; //持有锁超时时间
  6.    boolean success = lock.tryLock(5, lockTimeOut, TimeUnit.SECONDS);
  7.    if (success) {
  8.       try {
  9.           //业务逻辑实现
  10.       }catch (Exception e){
  11.           throw e;
  12.       } finally{
  13.           //释放锁
  14.           lock.unlock();
  15.       }
  16.    } else {
  17.        log.error("获取锁失败!更新失败!");
  18.        throw new BizException(ErrorCodeEnum.PROCESS_DATA_ERROR);
  19.    }
  20.  }

问题:高并发时,锁没有生效 

spring注解事务@Transactional分布式锁一起使用的问题
这是因为@Transactional是通过方法,是否抛出异常?来判断事务是否回滚,还是提交,此时方法已经结束。

但是我们必须在方法结束之后释放锁,因此在释放锁之后,此时事务还没提交,由于锁已经释放,其他进程可以获得锁,并从数据库查询地点标记数,但是此时,前一个进程没有提交数据

该进程查到的数据,不是最新的数据。

尽管这个过程只要很短的时间(实际测试过程中,这个过程只要几毫秒),但是高并发的情况,还是会出问题。

解决方案:可以手动控制事务的提交,可以控制在事务提交后释放锁

  1. public markScenicSpot(){
  2. RLock lock = redisson.getLock("Afanti_markScenicSpot_updateCountwantAndCountbeenLock_
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/1021314
推荐阅读
相关标签
  

闽ICP备14008679号