美文网首页
Redis 分布式锁

Redis 分布式锁

作者: 张云飞Vir | 来源:发表于2022-09-23 11:04 被阅读0次

    场景

    一般电商网站都会遇到秒杀、特价之类的活动,大促活动有一个共同特点就是访问量激增,在高并发下会出现成千上万人抢购一个商品的场景。虽然在系统设计时会通过限流、异步、排队等方式优化,但整体的并发还是平时的数倍以上,参加活动的商品一般都是限量库存,如何防止库存超卖,避免并发问题呢?分布式锁就是一个解决方案。

    “分布式锁”是用来解决分布式应用中“并发冲突”的一种常用手段,实现方式一般有基于zookeeper及基于redis二种

    讨论

    1、无锁

    先看下下面的代码,实现方式01,没有锁,当5个线程同时访问就乱了。

    private String impl01() {
        String stockStr = stringRedisTemplate.opsForValue().get("stock");
        if (StringUtils.isEmpty(stockStr)) return "库存为空";
        int stock = Integer.parseInt(stockStr);
        if (stock > 0) {
            int newStock = stock - 1;
            stringRedisTemplate.opsForValue().set("stock", newStock + "");
            System.out.printf("扣减成功 from %s to %s \n", stock, newStock);
            return "扣减成功";
        } else {
            System.out.printf("扣减失败 from %s \n", stock);
            return "扣减失败";
        }
    }
    
    

    首先想到是加 synchronized。 而 synchronized 锁是属于JVM级别的,也就是在在单个机器上有效。而在微服务架构下,一般都是集群部署,一个服务会部署到不同计算机上。就又有问题了。

    2、redis 分布式锁

    使用SETNX实现分布式锁,

        try{
          // 先获得 锁,如果获得失败,则提示错误码
          Boolean lock_lock = stringRedisTemplate.opsForValue().setIfAbsent("LOCK_LOCK", "1");
          if (!lock_lock) return "获得锁失败,无法继续";
    
          ....这里是上面的业务代码。略
    
        }finally{
          // 完事后释放 锁
          stringRedisTemplate.delete("LOCK_LOCK");
        }
    
    

    不过它也是有缺陷的:
    超时 设置的10s时间 时,导致未完成任务,就把锁释放了,就乱套了。准确的10s真的合适吗, 如果在真正高并发的场景下,可能锁就会面临“一直失效”或“永久失效”。
    1.存在请求释放锁时释放掉的并不是自己的锁
    2.超时时间过短,存在代码未执行完便自动释放

    3、Redisson实现分布式锁

    Redisson 是一个连接 Redis 的客户端包。充分的利用了 Redis 键值数据库提供的一系列优势,基于Java实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。比如 锁 等。

    引入依赖

    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.6.5</version>
    </dependency>
    

    代码

    private synchronized String impl06() {
           RLock rLock = redisson.getLock("LOCK_LOCK_2");
           try {
               rLock.lock();
    
               //----------------------
               String res = "";
               String stockStr = stringRedisTemplate.opsForValue().get("stock");
               if (StringUtils.isEmpty(stockStr)) return "库存为空";
               int stock = Integer.parseInt(stockStr);
               if (stock > 0) {
                   int newStock = stock - 1;
                   stringRedisTemplate.opsForValue().set("stock", newStock + "");
                   res = String.format("扣减成功 from %s to %s \n", stock, newStock);
                   System.out.println(res);
               } else {
                   res = String.format("扣减失败 from %s \n", stock);
                   System.out.println(res);
               }
               return res;
           } finally {
               // 完事后释放 锁
               rLock.unlock();
           }
       }
    
    @Bean
    RedissonClient Redisson() {
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://192.168.1.111:6379")
                .setDatabase(1);
        return Redisson.create(config);
    }
    
    

    在思考解决方案时我们首先想到CAP原则(一致性、可用性、分区容错性),

    • 那么现在的Redis就是满足AP(可用性、分区容错性),
    • 如果想要解决该问题我们就需要寻找满足CP(一致性、分区容错性)的分布式系统。比如 zookeeper

    并发量没有那么高,可以用zookeeper来做分布式锁,但是它的并发能力远远不如Redis。如果你对并发要求比较高的话,那就用Redis

    相关文章

      网友评论

          本文标题:Redis 分布式锁

          本文链接:https://www.haomeiwen.com/subject/tlcnortx.html