春招之-Redis篇(2)

优惠券秒杀:

RedisIdWorker类:

  /**
     * 开始的时间戳,2024-2-5
     */
    private final static long BEGIN_TIMESTAMP = 1707091200L;

    /**
     * 序列号位数
     */
    private final static int COUNT_BITS=32;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    public long nextId(String keyPrefix){
        //生成时间戳
        LocalDateTime now = LocalDateTime.now();
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timestamp = nowSecond-BEGIN_TIMESTAMP;

        //生成序列号
        //获取当天日期
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);
        //拼接并返回
        return timestamp<<COUNT_BITS | count;
    }

业务类:VoucherOrderServiceImpl

 @Autowired
    SeckillVoucherServiceImpl service;

    @Autowired
    RedisIdWorker worker;

    @Override
    @Transactional
    public Result seckillVoucher(Long voucherId) {
        //查询优惠券
        SeckillVoucher byId = service.getById(voucherId);

        //判断秒杀时间
        if (byId.getBeginTime().isAfter(LocalDateTime.now())) {
            //秒杀尚未开始,返回错误信息
            return Result.fail("秒杀尚未开始");
        }
        if(byId.getEndTime().isBefore(LocalDateTime.now())){
            //秒杀已结束,返回错误信息
            return Result.fail("秒杀已结束");
        }

        //判断库存是否充足
        if (byId.getStock()<1) {
            //库存不足
            return Result.fail("库存不足!");
        }

        //扣减库存
        boolean isSuccess = service.update().setSql("stock = stock - 1").eq("voucher_id", voucherId).update();
        if(!isSuccess){
            //扣减失败
            return Result.fail("库存不足");
        }

        //创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        //订单ID,使用RedisIdWorker
        long orderId = worker.nextId("order");
        voucherOrder.setId(orderId);
        //用户ID,用ThreadLocal中取出
        voucherOrder.setUserId(UserHolder.getUser().getId());
        //优惠券ID
        voucherOrder.setVoucherId(voucherId);
        boolean save = save(voucherOrder);
        //返回订单

        return Result.ok(orderId);
    }

订单超卖问题:

异常问题:多个线程抢购,库存有可能出现负数

解决方案一:乐观锁,添加版本号:添加version字段

简化乐观锁:修改时和查询时作stock比较:cas(Compare And Set)缺点:买不了多少

改进:判断库存大于0就减

一人一单:

用悲观锁:

1、获取锁,锁住userId

2、提交事务:

事务提交只能用代理对象(proxy),不能用目标对象(this),

(1)、添加依赖:

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

(2)、在启动类上添加注解:


@EnableAspectJAutoProxy(exposeProxy = true)

(3)、业务类Impl代码:

        Long userId = UserHolder.getUser().getId();
        synchronized (userId.toString().intern()){
            IVoucherOrderService proxy = (IVoucherOrderService)AopContext.currentProxy();
            return proxy.createVoucherOrder(voucherId);
        }

(4)、createVoucher方法:


    @Transactional
    public Result createVoucherOrder(Long voucherId) {
        Long userId = UserHolder.getUser().getId();
            //一人一单
            Integer count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
            if(count>0){
                //不能再抢优惠券了
                return Result.fail("用户已购买,不能重复");
            }

            //扣减库存,乐观锁
            boolean isSuccess = service.update().setSql("stock = stock - 1")
                    .eq("voucher_id", voucherId)
                    .gt("stock",0)
                    .update();
            if(!isSuccess){
                //扣减失败
                return Result.fail("库存不足");
            }


            //创建订单
            VoucherOrder voucherOrder = new VoucherOrder();
            //订单ID,使用RedisIdWorker
            long orderId = worker.nextId("order");
            voucherOrder.setId(orderId);
            //用户ID,用ThreadLocal中取出
            voucherOrder.setUserId(userId);
            //优惠券ID
            voucherOrder.setVoucherId(voucherId);
            boolean save = save(voucherOrder);
            //返回订单

            return Result.ok(orderId);
    }

3、释放锁

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值