zoukankan      html  css  js  c++  java
  • redis分布式锁

    部署在一个tomcat下

    1. 模拟一个减库存的代码 这段代码请求少的话是没有问题的,但是在高并发的场景下就会出现问题
      @RequestMapping("redisTest")
      public String redisTest() {
          String key = "count";
          //获取库存数量
          int count = Integer.parseInt(stringRedisTemplate.opsForValue().get(key));
          //有库存减少
          if (count > 0) {
              count = count - 1;
              stringRedisTemplate.opsForValue().set(key, String.valueOf(count));
              System.out.println("扣除成功,剩余数量:" + count);
          } else {
              System.out.println("扣除失败");
          }
          return "end";
      }
      
    2. 当前redis count的值为500
    3. 使用jmemter 200个线程同时访问

    4. 结果,可以看到有些库存并没有减掉
    5. 解决方案,加个方法锁(必须获取到锁才能进入,否则阻塞)
          @RequestMapping("redisTest")
      public synchronized String redisTest() {
          System.out.println(this);
      
          String key = "count";
          //获取库存数量
          int count = Integer.parseInt(stringRedisTemplate.opsForValue().get(key));
          //有库存减少
          if (count > 0) {
              count = count - 1;
              stringRedisTemplate.opsForValue().set(key, String.valueOf(count));
              System.out.println("扣除成功,剩余数量:" + count);
          } else {
              System.out.println("扣除失败");
          }
          return "end";
      }
      

    集群

    1. 以上是一个tomcat,如果有两个tomcat使用nginx代理做负载均衡指向8080和8081端口
    2. 本机启动两个客户端,启动jmemter测试,可以看到会有重复的剩余数量

    3. 此时我们就需要使用到redis的分布式锁来解决,这种也没有完美的解决方案
    /**
     * 分布式锁
     *    1.  stringRedisTemplate.opsForValue().setIfAbsent("key", "value");if (!flag){return "";}finally { stringRedisTemplate.delete("key");}   如果加锁后运维部署把程序kill了,其他线程永远进不来
     *   2. stringRedisTemplate.opsForValue().setIfAbsent("key", "value", 10, TimeUnit.SECONDS);finally { stringRedisTemplate.delete("key");}
     *   在超高并发时程序运行时间不一致,第一个线程15秒 10秒后锁自动解除程序还未运行完,第二个线程进来获得锁后第一个线程运行完把锁删除了  依次类推,释放的永远不是自己的锁
     *   3. 动态设置锁的方法String lockValue = UUID.randomUUID().toString();Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(lock, lockValue, 10, TimeUnit.SECONDS);if (!flag) { return ""; }
     *   if (lockValue.equals(stringRedisTemplate.opsForValue().get(lock))) { stringRedisTemplate.delete("key"); }, 这里还是会出现锁失效的可能
     *   4. 续命   获取锁后开一个定时器进行在锁自动释放前获取当前自己锁是否被释放 如果没有证明代码还没执行完,进行续命,若执行完删除当前定时器
     */
    

    redisson

    redis客户端,内部实现了很多对分布式锁的支持

    <dependency>
    	<groupId>org.redisson</groupId>
    	<artifactId>redisson</artifactId>
    	<version>3.13.2</version>
    </dependency>	
    
    @Bean
    public Redisson redisson() {
       Config config = new Config();
       config.useSingleServer().setAddress("redis://192.168.150.110").setDatabase(0);
        return ((Redisson) Redisson.create(config));
     }
        String lockKey = "key";
        RLock redissonLock = redisson.getLock(lockKey);
        try {
         	//内部使用续命的方式 使用lua脚本保证原子性
            redissonLock.lock();
            String countKey = "count";
            //获取库存数量
            int count = Integer.parseInt(stringRedisTemplate.opsForValue().get(countKey));
            //有库存减少
            if (count > 0) {
                count = count - 1;
                stringRedisTemplate.opsForValue().set(countKey, String.valueOf(count));
                System.out.println("扣除成功,剩余数量:" + count);
            } else {
                System.out.println("扣除失败");
            }
        } finally {
            redissonLock.unlock();
        }
        return "end";
    
    作者: JaminYe
    版权声明:本文原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
  • 相关阅读:
    网络流24题之圆桌问题
    BZOJ 4276: [ONTAK2015]Bajtman i Okrągły Robin
    网络流24题航空路线问题
    BZOJ1038 瞭望塔
    BZOJ4029 HEOI2015定价
    BZOJ1226 SDOI2009学校食堂
    网络流24题之魔术球问题
    网络流24题之最小路径覆盖问题
    【BZOJ1098】[POI2007]办公楼biu
    BZOJ3065 带插入区间K小值
  • 原文地址:https://www.cnblogs.com/JaminYe/p/13418106.html
Copyright © 2011-2022 走看看