redis优惠卷秒杀-------优化

原思路:

因为时串行执行且多次调用数据库,所以效率较慢,所以引入队列概念

优化思路:

 优化流程(1:库存不足,2:用户已经下单,无法重复下单确保一人一单,0:有下单资格)

 1.修改添加优惠卷业务,在添加优惠卷到数据库的同时添加到redis中

@Override
    @Transactional
    public void addSeckillVoucher(Voucher voucher) {
        // 保存优惠券
        save(voucher);
        // 保存秒杀信息
        SeckillVoucher seckillVoucher = new SeckillVoucher();
        seckillVoucher.setVoucherId(voucher.getId());
        seckillVoucher.setStock(voucher.getStock());
        seckillVoucher.setBeginTime(voucher.getBeginTime());
        seckillVoucher.setEndTime(voucher.getEndTime());
        seckillVoucherService.save(seckillVoucher);
        //保存优惠卷信息到redis中
        stringRedisTemplate.opsForValue().set(SECKILL_STOCK_KEY+voucher.getId(),voucher.getStock().toString());
    }

2.编写lua脚本,实现秒杀库存,一人一单,决定抢购是否成功


-- 1.参数列表
-- 1.1 . 优惠卷id
local voucherId= ARGV[1]
-- 1.2. 用户id
local userId = ARGV[2]

-- 2.数据key
--2.1 库存key
local stockKey = 'seckill:stock:' .. voucherId
--2.2 订单key
local orderKey = 'seckill:order:' .. voucherId


-- 3. 脚本业务
-- 3.1. 判断库存是否充足 get stockKey
if (tonumber(redis.call('get',stockKey)) <= 0 ) then
    -- 3.2. 库存不足,返回1
    return 1
end

-- 3.2. 判断用户是否下单 SISMEMBER orderKey userId
if (redis.call('SISMEMBER',orderKey,userId) == 1) then
    -- 3.3. 存在,说明是重复下单,返回2
    return 2
end
-- 3.4. 扣库存 incrby stockKey -1
redis.call('incrby',stockKey, -1)
-- 3.5. 下单(保存用户) sadd orderKey userId
redis.call('sadd',orderKey,userId)
return 0

 3.重写业务类

private IVoucherOrderService proxy;
    @Override
    public Result seckillVoucher(Long voucherId) {
        //获取用户
        Long userId = UserHolder.getUser().getId();
        //执行lua脚本
        Long result = stringRedisTemplate.execute(
                SECKILL_SCRIPT,//脚本文件
                Collections.emptyList(),//key的集合
                voucherId.toString(), userId.toString() //两个ARGV
        );
        //将返回值转换为int
        int i = result.intValue();
        //判断结果是否为零
        if (i != 0 ){
            //不为零,无法购买 ---1为库存不足,2为用户已经下单
            return Result.fail(i == 1 ? "库存不足" : "不能重复下单");
        }
        //可以购买,将下单信息保存到阻塞队列
        //生成订单id
        long orderId = redisIdWorker.nextId("order");
        // todo 保存到阻塞队列
        VoucherOrder voucherOrder = new VoucherOrder();
        //代金卷id
        voucherOrder.setVoucherId(voucherId);
        //id
        voucherOrder.setId(orderId);
        //用户id
        voucherOrder.setUserId(userId);
        //放入阻塞队列
        orderTasks.add(voucherOrder);
        //获取代理对象
        proxy = (IVoucherOrderService) AopContext.currentProxy();
        //返回订单id
        return Result.ok(orderId);
    }

4.完成异步下单


    //创建阻塞队列
    private BlockingQueue<VoucherOrder> orderTasks =new ArrayBlockingQueue<>(1024*1024);

    //创建线程池
    private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();

    @PostConstruct
    private void init(){
        SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());
    }

    private class VoucherOrderHandler implements Runnable{
        @Override
        public void run() {
            while (true){
                try {
                    //1. 获取队列中的订单信息
                    VoucherOrder voucherOrder = orderTasks.take();
                    //2.创建订单
                    handleVoucherOrder(voucherOrder);
                } catch (InterruptedException e) {
                    log.error("处理订单异常",e);
                }
            }

        }
    }

    /**
     * 创建订单业务(在异步处理阻塞队列时)
     * @param voucherOrder
     */
    private void handleVoucherOrder(VoucherOrder voucherOrder) {
        //1.获取用户
        Long userId = voucherOrder.getUserId();
        //2.获取锁对象
        RLock lock = redissonClient.getLock("lock:order:" + userId);
        //3.获取锁
        boolean isLock = lock.tryLock();
        //判断是否获取锁成功
        if (!isLock){
            //获取锁失败,记录日志
            log.error("不允许重复下单");
            return;
        }
        try {
            proxy.createVoucherOrder(voucherOrder);
        } finally {
            lock.unlock();
        }


    }
 @Transactional
    public  void createVoucherOrder(VoucherOrder voucherOrder) {
        //从拦截器中获取用户id
        Long userId = voucherOrder.getUserId();

        Long voucherId = voucherOrder.getVoucherId();
        /**
         * 进行判断,一人一单
         */
        int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
        if (count > 0) {
            //用户已经购买过商品
            log.error("用户已经购买过商品");
           return;
        }

        //扣减库存
        boolean update = iSeckillVoucherService.update().setSql("stock = stock - 1")
                .eq("voucher_id", voucherId)
                .gt("stock", 0)
                .update();
        //判断库存扣减是否成功
        if (!update) {
            //库存扣减失败,返回信息
            log.error("库存不足");
            return ;
        }

        System.out.println(voucherOrder.toString());

        //添加到数据库
        boolean save = save(voucherOrder);

        log.error("---------------------------");

        if (!save) {
            //添加失败
            log.error("添加失败");
            return ;
        }

    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值