• 基于 Redis 实现的分布式锁


    获取锁

    互斥:确保只有一个线程获得锁

    # 添加锁 利用setnx的互斥性
    127.0.0.1:6379> setnx lock thread1
    
    
    • 1
    • 2
    • 3

    释放锁

    手动释放锁
    超时释放:获取锁时设置一个超时时间

    #释放锁 删除即可
    127.0.0.1:6379> del lock
    
    
    • 1
    • 2
    • 3

    两步合成一步

     help set
    
    
      SET key value [EX seconds] [PX milliseconds] [NX|XX]
      summary: Set the string value of a key
      since: 1.0.0
      group: string
    
    
    127.0.0.1:6379> get k1
    (nil)
    127.0.0.1:6379> set lock k1 ex 5 nx
    OK
    127.0.0.1:6379> set lock k1 ex 5 nx
    nil
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述
    在这里插入图片描述

    分布式锁解决方案_Redis实现的分布式锁

    编写创建订单实现类

     @Override
      public String createOrderRedis(Integer productId, Integer count) throws Exception {
    
    
        log.info("*************** 进入方法 **********");
        String key = "lock:";
        String value = UUID.randomUUID().toString();
    
    
        // 获取分布式锁
        Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(key+productId, String.valueOf(Thread.currentThread().getId()),30,TimeUnit.SECONDS);
        // 判断是否获取锁成功
        if (!result){
          log.info("我进入了锁");
          return "不允许重复下单";
         }
        try {
          // 1、根据商品id查询商品信息
          Product product = productMapper.selectById(productId);
          // 2、判断商品是否存在
          if (product == null) {
            throw new RuntimeException("购买商品不存在:" + productId + "不存在");
           }
          // 3、校验库存
          if (count > product.getCount()) {
            throw new RuntimeException("商品" + productId + "仅剩" + product.getCount() + "件,无法购买");
           }
          // 4、计算库存
          Integer leftCount = product.getCount() - count;
          // 5、更新库存
          product.setCount(leftCount);
          productMapper.updateById(product);
          // 6、 创建订单
          TOrder order = new TOrder();
          order.setOrderStatus(1);//待处理
          order.setReceiverName("张三");
          order.setReceiverMobile("18587781068");
          order.setOrderAmount(product.getPrice().multiply(new BigDecimal(count)));//订单价格
          baseMapper.insert(order);
    
    
          // 7、 创建订单和商品关系数据
          OrderItem orderItem = new OrderItem();
          orderItem.setOrderId(order.getId());
          orderItem.setProduceId(product.getId());
          orderItem.setPurchasePrice(product.getPrice());
          orderItem.setPurchaseNum(count);
          orderItemMapper.insert(orderItem);
          return order.getId();
         }catch (Exception e){
          e.printStackTrace();
         }finally {
          // 释放锁
          stringRedisTemplate.delete(key+productId);
         }
        return "创建失败";
       }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    Redis分布式锁误删除问题

    Redis分布式锁误删除问题解决方案
    设置超时时间远大于业务执行时间,但是会带来性能问题
    删除锁的时候要判断,是不是自己的,如果是再删除

    配置锁标识

    private static final String KEY_PREFIX = "lock:";
      private static final String ID_PREFIX = UUID.randomUUID().toString().replace("-","");
    
    
    • 1
    • 2
    • 3

    获取锁

    
    
        //1、获取线程标识
        String threadId = ID_PREFIX + Thread.currentThread().getId();
     // 2、获得锁  setnx  key  value  time  type
        Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX+produceId, threadId, 30, TimeUnit.SECONDS);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    释放锁

    // 获取锁标识
          String s = stringRedisTemplate.opsForValue().get(KEY_PREFIX + produceId);
          // 判断标识是否一致
          if (s.equals(threadId)){
            // 释放锁
            stringRedisTemplate.delete(KEY_PREFIX + produceId);
           }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Redis分布式锁不可重入问题

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    分布式锁解决方案_基于Redisson实现的分布式锁实现

    Redisson介绍
    Redisson - 是一个高级的分布式协调Redis客服端,能帮助用户在分布式环境中轻松实现一些Java的对象,Redisson、Jedis、Lettuce 是三个不同的操作 Redis 的客户端,Jedis、Lettuce 的 API 更侧重对 Reids 数据库的 CRUD(增删改查),而 Redisson API 侧重于分布式开发。

    <dependency>
          <groupId>org.redisson</groupId>
          <artifactId>redisson-spring-boot-starter</artifactId>
          <version>3.17.2</version>
     </dependency>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    编写Redis分布式锁工具类

    package com.itbaizhan.lock.utils;
    
    
    import lombok.extern.slf4j.Slf4j;
    import org.redisson.api.RLock;
    import org.redisson.api.RedissonClient;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    
    import java.util.concurrent.TimeUnit;
    
    
    @Component
    @Slf4j
    public class DistributedRedisLock {
    
    
      @Autowired
      private RedissonClient redissonClient;
    
    
      // 加锁
      public Boolean lock(String lockName) {
        if (redissonClient == null) {
          log.info("DistributedRedisLock redissonClient is null");
          return false;
         }
    
    
        try {
          RLock lock = redissonClient.getLock(lockName);
          // 锁15秒后自动释放,防止死锁
          lock.lock(15, TimeUnit.SECONDS);
    
    
          log.info("Thread [{}] DistributedRedisLock lock [{}] success", Thread.currentThread().getName(), lockName);
          // 加锁成功
          return true;
         } catch (Exception e) {
          log.error("DistributedRedisLock lock [{}] Exception:", lockName, e);
          return false;
         }
       }
    
    
      // 释放锁
      public Boolean unlock(String lockName) {
        if (redissonClient == null) {
          log.info("DistributedRedisLock redissonClient is null");
          return false;
         }
    
    
        try {
          RLock lock = redissonClient.getLock(lockName);
          lock.unlock();
          log.info("Thread [{}] DistributedRedisLock unlock [{}] success", Thread.currentThread().getName(), lockName);
          // 释放锁成功
          return true;
         } catch (Exception e) {
          log.error("DistributedRedisLock unlock [{}] Exception:", lockName, e);
          return false;
         }
       }
    }
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    编写创建订单接口实现

     /**
       * Redis锁实现
       *
       * @param productId
       * @param count
       * @return
       * @throws Exception
       */
      @Override
      public String createOrderRedis(Integer productId, Integer count) throws Exception {
        //获取锁对象
        if (distributedRedisLock.lock(String.valueOf(productId))) {
          try {
            // 1、根据商品id查询商品信息
            Product product = productMapper.selectById(productId);
            // 2、判断商品是否存在
            if (product == null) {
              throw new RuntimeException("购买商品不存在:" + productId + "不存在");
             }
            // 3、校验库存
            if (count > product.getCount()) {
              throw new RuntimeException("商品" + productId + "仅剩" + product.getCount() + "件,无法购买");
             }
            // 4、计算库存
            Integer leftCount = product.getCount() - count;
            // 5、更新库存
            product.setCount(leftCount);
            productMapper.updateById(product);
            // 6、 创建订单
            TOrder order = new TOrder();
            order.setOrderStatus(1);//待处理
            order.setReceiverName("张三");
            order.setReceiverMobile("18587781068");
            order.setOrderAmount(product.getPrice().multiply(new BigDecimal(count)));//订单价格
            baseMapper.insert(order);
            // 7、 创建订单和商品关系数据
            OrderItem orderItem = new OrderItem();
            orderItem.setOrderId(order.getId());
            orderItem.setProduceId(product.getId());
            orderItem.setPurchasePrice(product.getPrice());
            orderItem.setPurchaseNum(count);
            orderItemMapper.insert(orderItem);
            return order.getId();
           } catch (Exception e) {
            e.printStackTrace();
           } finally {
            distributedRedisLock.unlock(String.valueOf(productId));
           }
         }
        return "创建失败";
       }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
  • 相关阅读:
    一文2600字教你接口测试基本知识点(超全)
    TAP 系列文章8 | TAP 学习中心——通过动手教程来学习
    传出神经系统分为哪两类,传出神经的分类与功能
    跟着顶级科研报告IPCC学绘图:温度折线/柱图/条带/双y轴
    蓝牙Mesh系统开发四 ble mesh网关节点管理
    设计模式(二十七)----行为型模式之解释器模式
    解密负载均衡技术和负载均衡算法
    测试设计场景题
    软件接口安全设计规范及审计要点
    Xilinx IDDR与ODDR原语的使用
  • 原文地址:https://blog.csdn.net/weixin_44569326/article/details/134409643