Redis实战(黑马点评--优惠券秒杀)

一、redis实现全局唯一订单id

1、问题:使用数据库自增id不合适

当用户抢购商品时,生成的订单会保存到tb_voucher_order表中,而订单表如果使用数据库自增ID就会存在一些问题

  1. id规律性太明显
  2. 受单表数据量的限制

2、解决方法:全局id生成器

  • 符号位:1bit,永远为0
  • 时间戳:31bit,以秒为单位,可以使用69年(2^31秒约等于69年)
  • 序列号:32bit,秒内的计数器,支持每秒传输2^32个不同ID
 
复制代码
@Component
public class RedisIdWorker {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    //设置起始时间,我这里设定的是2022.01.01 00:00:00
    public static final Long BEGIN_TIMESTAMP = 1640995200L;
    //序列号长度
    public static final Long COUNT_BIT = 32L;

    public long nextId(String keyPrefix){
        //1. 生成时间戳
        LocalDateTime now = LocalDateTime.now();
        long currentSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timeStamp = currentSecond - BEGIN_TIMESTAMP;
        //2. 生成序列号
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        long count = stringRedisTemplate.opsForValue().increment("inc:"+keyPrefix+":"+date);
        //3. 拼接并返回,简单位运算
        return timeStamp << COUNT_BIT | count;
    }

    public static void main(String[] args) {
        //设置一下起始时间,时间戳就是起始时间与当前时间的秒数差
        LocalDateTime tmp = LocalDateTime.of(2022, 1, 1, 0, 0, 0);
        System.out.println(tmp.toEpochSecond(ZoneOffset.UTC));
        //结果为1640995200L
    }
}
复制代码

二、实现秒杀下单

脑图:

 

1、出现的问题(最后一张票时):多线程并行查询库存后,再减库存,会导致超卖。

2、解决方法:乐观锁,执行更新库存时,先比较:查询到的优惠券库存和实际数据库中优惠券库存是否相同

// 5.扣减库存
        boolean success = seckillVoucherService.update().setSql("stock = stock - 1")   // set stock = stock - 1
                .eq("voucher_id", voucherId).eq("stock", voucher.getStock())   // where id = ? and stock = ?
                .update();
        if (!success) {
            return Result.fail("库存不足");
        }
  • 以上逻辑的核心含义是:只要我扣减库存时的库存和之前我查询到的库存是一样的,就意味着没有人在中间修改过库存,那么此时就是安全的,但是以上这种方式通过测试发现会有很多失败的情况,失败的原因在于:在使用乐观锁过程中假设100个线程同时都拿到了100的库存,然后大家一起去进行扣减,但是100个人中只有1个人能扣减成功,其他的人在处理时,他们在扣减时,库存已经被修改过了,所以此时其他线程都会失败
  • 那么我们继续完善代码,修改我们的逻辑,在这种场景,我们可以只判断是否有剩余优惠券,即只要数据库中的库存大于0,都能顺利完成扣减库存操作
// 5.扣减库存
        boolean success = seckillVoucherService.update().setSql("stock = stock - 1")   // set stock = stock - 1
                .eq("voucher_id", voucherId) //.eq("stock", voucher.getStock())   // where id = ? and stock = ?
                .gt("stock", 0)
                .update();
        if (!success) {
            return Result.fail("库存不足");
        }

 

posted @   Anne起飞记  阅读(75)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体
点击右上角即可分享
微信分享提示