zoukankan      html  css  js  c++  java
  • spring boot:用redis+redisson实现分布式锁(redisson3.11.1/spring boot 2.2)

    一,为什么要使用分布式锁? 

    如果在并发时锁定代码的执行,java中用synchronized锁保证了线程的原子性和可见性
    但java锁只在单机上有效,如果是多台服务器上的并发访问,则需要使用分布式锁,
    例如:两台机器上同时各有一个进程查询同一件商品的库存,此时商品库存数为1,
    数据库给两台机器返回的都是1,
    然后这两台机器同时下单,两个订单就超出了商品的库存数,
    所以此时要使用分布式锁
     

    说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest

             对应的源码可以访问这里获取: https://github.com/liuhongdi/

    说明:作者:刘宏缔 邮箱: 371125307@qq.com

     

    二,使用redisson

    redisson是对redis用java语言的封装
    1,redisson的官网:
    https://redisson.org/
    2,redisson的官方文档:
    https://github.com/redisson/redisson/wiki
    3,使用redisson做分布式锁和mysql悲观锁(for update)的区别:
       本质上没有区别,
       但redis性能更强
     

    三,演示项目的相关信息

    1,地址
    https://github.com/liuhongdi/distributedlock
    2,原理:
      在减库存之前,先加锁,
      在减库存完成后,解锁
      这样避免高并发时查询到相同的库存数而导致超卖情况
     
    3,结构
     
     
    4,数据表:
    CREATE TABLE `goods` (
      `goods_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
      `goods_name` varchar(500) NOT NULL DEFAULT '' COMMENT 'name',
      `stock` int(11) NOT NULL DEFAULT '0' COMMENT 'stock',
      PRIMARY KEY (`goods_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表'

     四,java代码的说明

    OrderServiceImpl.java
    @Service
    public class OrderServiceImpl implements OrderService {
     
        @Resource
        private RedissonClient redissonClient;
        @Resource
        private GoodsMapper goodsMapper;
     
        /*
        *   加锁减库存
        * */
        @Override
        public boolean decrementProductStoreLock(int goodsId, int buyNum) {
            String key = "dec_store_lock_" + goodsId;
            //生成锁对象
            RLock lock = redissonClient.getLock(key);
            try {
                //2, TimeUnit.MINUTES
                lock.lock(2, TimeUnit.MINUTES);
                boolean upRes = updateGoodsStock(goodsId, buyNum);
                if (upRes == false) {
                    return false;
                }
            } catch (Exception e) {
                System.out.println(e.getMessage());
                return false;
            } finally {
                //解锁
                if (lock.isHeldByCurrentThread()){
                    System.out.println("----------------release lock");
                    lock.unlock();
                }
            }
            return true;
        }
     
        /*
         *   减库存
         * */
        @Transactional(isolation = Isolation.REPEATABLE_READ)
        public boolean updateGoodsStock(int goodsId, int buyNum) {
            Goods goodsOne = goodsMapper.selectOneGoods(goodsId);
            System.out.println("-------------------------当前库存:"+goodsOne.getStock()+"-------购买数量:"+buyNum);
            if (goodsOne.getStock() < buyNum || goodsOne.getStock() <= 0) {
                System.out.println("------------------------fail:buy fail,return");
                return false;
            }
            int upStock = goodsOne.getStock()-buyNum;
            goodsOne.setStock(upStock);
            int upNum = goodsMapper.updateOneGoodsStock(goodsOne);
            System.out.println("-------------------------success:成交订单数量:"+upNum);
            return true;
        }
     
        /*
         *   不加锁减库存
         * */
        @Override
        public boolean decrementProductStoreNoLock(int goodsId, int buyNum) {
            return updateGoodsStock(goodsId, buyNum);
        }
    }
     
    java代码说明:
    String key = "dec_store_lock_" + goodsId:  加锁时加入商品id,这样不影响其他商品
    RLock是redisson中定义的可重入锁
     lock.lock(2, TimeUnit.MINUTES)   加锁,时长两分钟
    lock.unlock(); 解锁
     

    五,测试高并发时加锁的效果:

    1,数据库的数据:
    mysql> update goods set stock=3;
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
     
    mysql> select * from goods;
    +----------+------------+-------+
    | goods_id | goods_name | stock |
    +----------+------------+-------+
    |        3 | green cup2 |     3 |
    +----------+------------+-------+
    1 row in set (0.00 sec)

    我们设置商品的库存数为3

    2,不加锁的情况
    #-c:请求并发数
    #-n:请求总数
    [root@localhost ~]# ab -c 20 -n 20 http://127.0.0.1:8080/lock/buynolock

    查看代码的打印输出: 

    -------------------------当前库存:3-------购买数量:1
    -------------------------success:成交订单数量:1
    -------------------------当前库存:2-------购买数量:1
    -------------------------success:成交订单数量:1
    -------------------------当前库存:1-------购买数量:1
    -------------------------当前库存:1-------购买数量:1
    -------------------------当前库存:1-------购买数量:1
    -------------------------success:成交订单数量:1
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------success:成交订单数量:1
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------success:成交订单数量:1
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    共成交5单,超出了库存数量
     
    3,加锁的情况:
    #-c:请求并发数
    #-n:请求总数
    [root@localhost ~]# ab -c 20 -n 20 http://127.0.0.1:8080/lock/buylock

    查看代码的打印输出: 

    -------------------------当前库存:3-------购买数量:1
    -------------------------success:成交订单数量:1
    -------------------------当前库存:2-------购买数量:1
    -------------------------success:成交订单数量:1
    -------------------------当前库存:1-------购买数量:1
    -------------------------success:成交订单数量:1
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return
    -------------------------当前库存:0-------购买数量:1
    ------------------------fail:buy fail,return

    只成功了3个订单,说明分布式锁有效

    六,查看spring boot的版本:

      .   ____          _            __ _ _
     /\ / ___'_ __ _ _(_)_ __  __ _    
    ( ( )\___ | '_ | '_| | '_ / _` |    
     \/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.2.0.RELEASE)
  • 相关阅读:
    删除 node_modules文件夹cmd指令
    vue 限制输入字符长度
    vertical-align和text-align属性实现垂直水平居中
    二分查找法
    MySQL实现分页查询
    数据库连接
    AOP编程的常用实现方式
    链表中环的入口
    AQS同步组件及ReentrantLock和synchronized的区别
    快速排序的递归和非递归
  • 原文地址:https://www.cnblogs.com/architectforest/p/13140352.html
Copyright © 2011-2022 走看看