当前位置:   article > 正文

黑马点评--Redis优化秒杀_黑马点评redis

黑马点评redis

Redis优化秒杀

异步秒杀思路:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xd5i89Jh-1669207961602)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221120161810736.png)]

思路,在多线程同时秒杀时,由于判断秒杀库存和校验一人一单用时短,而减库存和创建订单时间耗时长。

所以采用同步思路就浪费时间。如果采用异步思路就能:在redis中判断秒杀库存和校验一人一单,在tomcat中读取队列中的信息对数据库进行操作。在redis返回结果,Tomcat判断生成id并返回。大大提高用户体验。

Redis的操作通过Lua脚本保证原子性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8xsz5DU9-1669207961604)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221120165237766.png)]

改进秒杀业务,提高并发性能:

需求:

新增秒杀优惠券的同时,将优惠券保存到Redis中

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());
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

基于Lua脚本,判断秒杀库存,一人一单,决定用户是否抢购成功,

如果抢购成功,将优惠劵id和用户id封装后存入阻塞队列

**基于Redis完成秒杀资格判断:**
 //执行lua脚本
    private static final DefaultRedisScript<Long> SECKILL_SCRIPT ;
    static {
        SECKILL_SCRIPT =new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }
    /**
     * 异步基于lua脚本实现抢购
     * @param voucherId
     * @return
     */
    @Override
    public Result seckillVoucher(Long voucherId) {
        //获取用户
        Long userId = UserHolder.getUser().getId();
        //1.执行lua脚本
        Long result = stringRedisTemplate.execute(
                SECKILL_SCRIPT,
                Collections.emptyList(),
                voucherId.toString(),
                userId.toString()
        );
        //2.判断结果是为0
        int r = result.intValue();
        if (r !=0 ){
            //2.1.不为0,代表没有购买资格
            return  Result.fail(r==1 ? "库存不足":"不能重复下单");
        }
        //2.2.为0,有购买资格,把下单信息保存到阻塞队列
        long orderId = redisIdWorker.nextId("order");
        //todo 保存阻塞队列
        //3.返回订单id
        return Result.ok(orderId);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

seckill.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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

开启线程任务,不断从阻塞队列中获取信息,实现异步下单功能

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

    //线程池
    private static final ExecutorService seckill_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();

    //当前类初始化完了就执行init方法
    @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 (Exception e) {
                    log.error("处理订单异常",e);
                }
            }
        }
    }
    private void handleVoucherOrder(VoucherOrder voucherOrder) {
        //1.获取用户
        Long userId =voucherOrder.getUserId();
        //2.创建锁对象
        RLock lock = redissonClient.getLock("lock:order:"+userId);
        //3.获取锁
        boolean isLock =lock.tryLock();
        //4.判断是否获取锁成功
        if (!isLock){
            //获取锁失败,返回错误或重试
            log.error("不允许重复下单");
        }
        try {
            proxy.createVoucherOrderYiBu(voucherOrder);
        }finally {
            //释放锁
            lock.unlock();
        }
    }

    public void createVoucherOrderYiBu(VoucherOrder voucherOrder){
        //6.一个人一单
        Long userId = voucherOrder.getUserId();
        //6.1查询订单
        int count = query().eq("user_id", userId).eq("voucher_id", voucherOrder.getVoucherId()).count();
        //6.2判断是否存在
        if (count > 0) {
            //用户以及购买过
            log.error("用户已经购买过一次");
            return;
        }
        //7.扣减库存
        boolean success = iSeckillVoucherService.update()
                .setSql("stock =stock -1")
                .eq("voucher_id", voucherOrder.getVoucherId())
                .gt("stock", 0).update();
        if (!success) {
            //扣减失败
            log.error("库存不足");
            return ;
        }
        save(voucherOrder);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/985793
推荐阅读
相关标签
  

闽ICP备14008679号