Redis+Lua脚本实现商品库存扣减

因为Redis是单线程执行,所以Lua脚本的执行具有原子性

库存初始化:

 -3:库存未初始化 大于等于0:剩余库存(新增之后剩余的库存)
String ADD_INVENTORY =
              "if (redis.call('exists', KEYS[1]) == 1) then"
            + "    local occStock = tonumber(redis.call('hget', KEYS[1], KEYS[2]));"
            + "    if (occStock == nil) then"
            + "        return tonumber(redis.call('get', KEYS[3]));"
            + "    end;"
            + "    if (redis.call('hdel', KEYS[1], KEYS[2]) == 1) then"
            + "        return redis.call('incrBy', KEYS[3], occStock);"
            + "    end;"
            + "    return tonumber(redis.call('get', KEYS[3]));"
            + "end;"
            + "return -3;";

操作库存:

-5:库存操作失败 
-4:代表库存传进来的值是负数(非法值) 
-3:库存未初始化 
-2:库存不足 
-1:库存为0大于等于0:剩余库存(扣减之后剩余的库存)
String REDUCE_INVENTORY = 
              "if (redis.call('exists', KEYS[1]) == 1) then"
            + "    local stock = tonumber(redis.call('get', KEYS[1]));"
            + "    local num = tonumber(ARGV[1]);"
            + "    if (num <= 0) then"
            + "        return -4;"
            + "    end;"
            + "    if (stock <= 0) then"
            + "        return -1;"
            + "    end;"
            + "    if (stock >= num) then"
            + "        if (redis.call('hset', KEYS[2], KEYS[3], num) == 1) then"
            + "            return redis.call('incrBy', KEYS[1], 0 - num);"
            + "        else"
            + "            return -5;"
            + "        end;"
            + "    end;"
            + "    return -2;"
            + "end;"
            + "return -3;";

初始缓存库存:

public Boolean initCacheStock(Integer productId, Integer stockNum, StockCacheTypeEnum stockCacheTypeEnum) {
        String stockKey= String.format(RedisKeyConstants.GOODS_CACHE_STOCK_KEY, productId);
        switch (stockCacheTypeEnum) {
            case INIT: {
                boolean result = redisUtil.setIfAbsent(stockKey, stockNum);
                if (result) {
                    log.info("初始化商品[{}]缓存库存成功, 数量为:{}", productId, redisUtil.get(stockKey));
                    return Boolean.TRUE;
                }
                log.info("商品[{}]已存在缓存库存", productId);
            }
            case RESET: {
                boolean result = redisUtil.setIfPresent(stockKey, stockNum);
                if (result) {
                    log.info("重置商品[{}]缓存库存成功, 数量为:{}", productId, redisUtil.get(stockKey));
                    return Boolean.TRUE;
                }
                String msg = StrUtil.format("重置商品[{}]缓存库存失败", productId);
                log.info(msg);
                throw new ShopRuntimeException(msg);
            }
            default:
                throw new ShopRuntimeException("商品库存未知操作类型");
        }
    }

添加库存:

public boolean addCacheStock(Integer productId, String orderNo, Integer memberId) {
        String stockKey= String.format(RedisKeyConstants.GOODS_CACHE_STOCK_KEY, productId);
        String occupyStockKey= String.format(RedisKeyConstants.GOODS_CACHE_STOCK_OCCUPY_KEY, memberId);
        Long result = redisTemplate.execute(addInventoryScript, CollUtil.toList(occupyStockKey, orderNo, stockKey));
        if (Objects.isNull(result)) {
            log.info("增加商品[{}]缓存库存失败", productId);
            return Boolean.FALSE;
        }
        if (result >= 0) {
            log.info("商品[{}] 剩余缓存库存:[{}]", productId, result);
            return Boolean.TRUE;
        }
        String desc = StockCacheStatusEnum.getValueOfCode(result);
        log.info("增加商品[{}]缓存库存失败: {}", productId, desc);
        throw new ShopRuntimeException(desc);
    }

扣减库存:

public Boolean reduceCacheStock(Integer productId, Integer stockNum, String orderNo, Integer memberId) {
        String stockKey= String.format(RedisKeyConstants.GOODS_CACHE_STOCK_KEY, productId);
        String occupyStockKey= String.format(RedisKeyConstants.GOODS_CACHE_STOCK_OCCUPY_KEY, memberId);
        Long result = redisTemplate.execute(reduceInventoryScript, CollUtil.toList(stockKey, occupyStockKey, orderNo), stockNum);
        if (result == null) {
            log.info("扣减商品[{}]缓存库存失败", productId);
            return Boolean.FALSE;
        }
        if (result >= 0) {
            log.info("商品[{}] 本次扣减缓存库存:[{}], 剩余缓存库存:[{}]", productId, stockNum, result);
            return Boolean.TRUE;
        }
        String desc = StockCacheStatusEnum.getValueOfCode(result);
        log.info("扣减商品[{}]缓存库存失败: {}", productId, desc);
        throw new ShopRuntimeException(desc);
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值