decr(互斥,一般不会产生超卖)
decr 命令在 Redis 中的主要好处包括: 原子性操作:decr 是一个原子操作,这意味着即使在高并发环境下,对于同一 key 的递减操作也是互斥的,不会出现竞态条件(race condition),保证了数据的一致性和完整性。 高性能计算:在 Redis 这种内存数据库中,执行 decr 操作非常迅速,相比起每次都需要从数据库读取数据、修改后再写回的传统数据库操作,Redis 可以显著提升计数器类业务场景的性能。 简化开发:在诸如统计访问量、商品库存扣减、积分系统中扣除积分等应用场景下,直接调用 decr 命令可以大大简化开发工作,无需编写额外的加减逻辑和事务处理代码。 空间效率:由于 Redis 内部实现了对整数值的优化存储,使用 decr 更新整数值时,仅需修改内存中的数值,不需要像文本或者其他复杂类型那样重新分配存储空间。
setnx 加锁兜底
setnx 加锁是一种兜底手段,避免后续有库存的恢复,(rediss挂了,人为恢复)导致库存从96消耗后又回到了98重复消费。所以对于每个key加锁,98、97、96... 即使有恢复库存也不会导致超卖。【setnx 在 redisson 是用 trySet 实现】
redissonClient.getBucket(key).trySet("lock")
在 Redisson 中,Bucket 类用于操作单个值的 Redis 键,其 trySet(V value) 方法尝试将桶(bucket)的值设置为给定值,如果 bucket 之前没有值,则设置成功并返回 true;如果已经有值,则设置失败并返回 false。
延迟消费队列
// 生成消费队列
@Override public void awardStockConsumeSendQueue(StrategyAwardStockKeyVO strategyAwardStockKeyVO) { String cashKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_QUERY_KEY; RBlockingQueue<Object> blockingQueue = redisService.getBlockingQueue(cashKey); RDelayedQueue<Object> delayedQueue = redisService.getDelayedQueue(blockingQueue); delayedQueue.offer(strategyAwardStockKeyVO,3, TimeUnit.SECONDS); }
消费队列是在RuleStockLogicTreeNode(规则树)调用生成, !!!相当于权重装配没有走库存,不进行库存扣减
定时任务
Cron 表达式 "0/5 * * * * ?" 解释如下: 第一位 0:表示分钟域的值,这里意味着从第 0 分钟开始,每隔 5 分钟执行一次(即 0, 5, 10, 15, ...)。 接下来的四个星号 * 分别代表小时域、月份中的日期域、月份域、星期域,均表示任意值,即不作任何限制。 最后一个问号 ? 在某些 Cron 表达式实现中表示月份中的日期(day-of-month)和星期(day-of-week)两个字段不必同时满足,只要其中之一满足条件即可执行任务。但在 Spring 的 @Scheduled 注解中,它通常用来兼容 Quartz Cron 表达式,并无特殊含义。 因此,整个表达式意味着该方法将会每 5 分钟执行一次,不受日期、小时或其他日历因素影响。
1.拿消费队列
2.更新数据库库存
部分代码解释
@Override public Long getAtomicLong(String key) { return redissonClient.getAtomicLong(key).get(); } 该函数通过Redisson客户端获取具有指定键的原子长整型对象,并返回该对象的当前值。 @Override public void setAtomicLong(String cacheKey, Integer awardCount) { redissonClient.getAtomicLong(cacheKey).set(awardCount); } 该函数使用Redisson客户端获取具有给定缓存键的AtomicLong对象,并将指定的奖品数量设置为该AtomicLong对象的值。
! decr setNx都是在这一步
public Boolean subtractionAwardStock(String cacheKey) { //扣减库存(存的是总库存,为了加锁) long surplus = redisService.decr(cacheKey); if (surplus < 0) { redisService.setValue(cacheKey,0); return false; }; String lockKey = cacheKey + Constants.UNDERLINE + surplus; Boolean lock= redisService.setNx(lockKey); if (!lock) { log.info("策略奖品库存加锁失败,", lockKey); } return lock; }