并发情况下的库存扣减

一: 有并发情况下商品的库存扣减可以采用分段锁的方式:

public Boolean subtractionAwardStock(String cacheKey, Date endDateTime) {
    try {
        long surplus = redisService.decr(cacheKey);
        if (surplus == 0) {
            // 库存消耗没了以后,发送MQ消息,更新数据库库存
            eventPublisher.publish(activitySkuStockZeroMessageEvent.topic(), activitySkuStockZeroMessageEvent.buildEventMessage(sku));
        } else if (surplus < 0) {
            // 库存小于0,恢复为0个
            redisService.setAtomicLong(cacheKey, 0);
            return false;
        }
		// 1. 按照cacheKey decr 后的值,如 99、98、97 和 key 组成为库存锁的key进行使用。
        // 2. 加锁为了兜底,如果后续有恢复库存,手动处理等,也不会超卖。因为所有的可用库存key,都被加锁了。
        // 生成锁的键
        String lockKey = cacheKey + Constants.UNDERLINE + surplus;

        // 设置合理的过期时间
        long expireMillis = calculateExpireMillis(endDateTime);
        Boolean lock = redisService.setNx(lockKey, expireMillis, TimeUnit.MILLISECONDS);

        if (!lock) {
            log.info("策略奖品库存加锁失败 {}", lockKey);
        }
        return lock;
    } catch (Exception e) {
        log.error("扣减库存并加锁失败", e);
        return false;
    }
}

private long calculateExpireMillis(Date endDateTime) {
    if (endDateTime != null) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(endDateTime);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 0);
        long endOfDayMillis = calendar.getTimeInMillis();
        long now = System.currentTimeMillis();
        return Math.max(endOfDayMillis - now, TimeUnit.MINUTES.toMillis(1)); // 至少1分钟过期时间
    } else {
        return TimeUnit.MINUTES.toMillis(1); // 默认1分钟过期时间
    }
}


二: 在活动已经开始并且已经扣减库存的情况下,如果运营临时要求增加库存,我们需要考虑以下几个方面:

1 增加库存:使用INCRBY命令增加库存数量。
2 处理分段锁:由于之前已经加了一部分分段锁,增加库存后可能会导致库存数量跨越多个分段锁,需要重新评估和调整这些锁。

public Boolean incrementAwardStock(String cacheKey, int increment, Date endDateTime) {
    try {
        // 增加库存
        long newStock = redisService.incrBy(cacheKey, increment);

        // 获取当前库存的分段锁
        for (int i = 0; i < increment; i++) {
            String currentLockKey = cacheKey + Constants.UNDERLINE + (newStock - increment + i);
            redisService.del(currentLockKey); // 释放旧的分段锁
        }

        return true;
    } catch (Exception e) {
        log.error("增加库存并释放锁失败", e);
        return false;
    }
}

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@淡 定

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值