首页 > 其他分享 >秒杀优化-基于阻塞队列实现秒杀优化

秒杀优化-基于阻塞队列实现秒杀优化

时间:2024-07-27 10:55:40浏览次数:7  
标签:return voucherOrder 队列 userId private 秒杀 Long 优化 Result

秒杀优化

VoucherOrderServiceImpl

修改下单动作,现在我们去下单时,是通过lua表达式去原子执行判断逻辑,如果判断我出来不为0 ,则要么是库存不足,要么是重复下单,返回错误信息,如果是0,则把下单的逻辑保存到队列中去,然后异步执行

@Slf4j
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {

    @Autowired
    private ISeckillVoucherService seckillVoucherService;

    @Autowired
    private RedisIdWorker redisIdWorker;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;

    static {
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }

    private BlockingQueue<VoucherOrder> orderTasks = new ArrayBlockingQueue<>(1024 * 1024);

    // 异步处理线程池
    private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();

    private IVoucherOrderService proxy;

    // 在类初始化之后执行,因为当这个类初始化好了之后,随时都是有可能要执行的
    @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 redisLock = redissonClient.getLock("lock:order:" + userId);

        // 3. 尝试获取锁
        boolean isLock = false;
        try {
            // 尝试获取锁,设置等待时间和锁自动释放时间
            // 如果锁不可用,则等待 1 秒钟;如果锁可用,则获取锁并设置锁自动释放时间为 10 秒
            isLock = redisLock.tryLock(1, 10, TimeUnit.SECONDS);

            // 4. 判断是否获得锁成功
            if (!isLock) {
                // 获取锁失败,直接返回失败或者重试
                log.error("不允许重复下单!");
                return;
            }
            // 注意:由于 Spring 的事务管理是放在 ThreadLocal 中,此时是多线程环境,事务可能会失效
            proxy.createVoucherOrder(voucherOrder);
        } catch (InterruptedException e) {
            // 如果线程被中断,处理中断异常
            Thread.currentThread().interrupt();
            log.error("线程被中断", e);
        } finally {
            // 释放锁
            if (isLock) { // 只有当成功获取锁时才释放锁
                redisLock.unlock();
            }
        }
    }

    @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()
        );
        int r = result.intValue();
        // 2.判断结果是否为0
        if (r != 0) {
            // 2.1.不为0 ,代表没有购买资格
            return Result.fail(r == 1 ? "库存不足" : "不能重复下单");
        }
        VoucherOrder voucherOrder = new VoucherOrder();
        // 2.3.订单id
        Long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
        // 2.4.用户id
        voucherOrder.setUserId(userId);
        // 2.5.代金券id
        voucherOrder.setVoucherId(voucherId);
        // 2.6.放入阻塞队列
        orderTasks.add(voucherOrder);
        // 3.获取代理对象
        proxy = (IVoucherOrderService) AopContext.currentProxy();
        // 3.返回订单id
        return Result.ok(orderId);
    }

/*     @Override
    public Result seckillVoucher(Long voucherId) {
        // 1.查询优惠券
        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
        // 2.判断秒杀是否开始
        if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
            // 尚未开始
            return Result.fail("秒杀尚未开始!");
        }
        // 3.判断秒杀是否已经结束
        if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
            // 尚未开始
            return Result.fail("秒杀已经结束!");
        }
        // 4.判断库存是否充足
        if (voucher.getStock() < 1) {
            // 库存不足
            return Result.fail("库存不足!");
        }

        Long userId = UserHolder.getUser().getId();

        // synchronized (userId.toString().intern()) {

        // 尝试创建锁对象
        // SimpleRedisLock lock = new SimpleRedisLock("order:" + userId, stringRedisTemplate);
        RLock lock = redissonClient.getLock("order:" + userId);
        // 获取锁
        boolean isLock = lock.tryLock();
        // boolean isLock = lock.tryLock();

        if (!isLock) {
            Result.fail("不允许重复下单");
        }
        // 获取代理对象 (事务)
        try {
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.createVoucherOrder(voucherId);
        } finally {
            lock.unlock();
        }
        // }

    } */

    @Transactional
    public void createVoucherOrder(VoucherOrder voucherOrder) {
        Long userId = voucherOrder.getUserId();
        // 5.1.查询订单
        int count = query().eq("user_id", userId).eq("voucher_id", voucherOrder.getVoucherId()).count();
        // 5.2.判断是否存在
        if (count > 0) {
            // 用户已经购买过了
            log.error("用户已经购买过了");
            return;
        }

        // 6.扣减库存
        boolean success = seckillVoucherService.update()
                .setSql("stock = stock - 1") // set stock = stock - 1
                .eq("voucher_id", voucherOrder.getVoucherId()).gt("stock", 0) // where id = ? and stock > 0
                .update();
        if (!success) {
            // 扣减失败
            log.error("库存不足");
            return;
        }
        save(voucherOrder);

    }
}

小总结:

秒杀业务的优化思路是什么?

  • 先利用Redis完成库存余量、一人一单判断,完成抢单业务
  • 再将下单业务放入阻塞队列,利用独立线程异步下单
  • 基于阻塞队列的异步秒杀存在哪些问题?
    • 内存限制问题
    • 数据安全问题

标签:return,voucherOrder,队列,userId,private,秒杀,Long,优化,Result
From: https://www.cnblogs.com/NorthPoet/p/18326729

相关文章

  • 单调栈和单调队列
    单调栈和单调队列P5788#include<bits/stdc++.h>usingnamespacestd;constintN=3e6+5;intn,a[N],ans[N],top,stk[N];intmain(){ scanf("%d",&n); for(inti=1;i<=n;++i){ scanf("%d",&a[i]); while(top>0&&a[stk[......
  • 【独家首发】Matlab实现凌日优化算法TSOA优化Transformer-BiLSTM实现负荷数据回归预测
    %假设您有负荷数据load_data和相应的回归标签regression_labels%1.数据预处理%在这一步中,您需要对负荷数据进行适当的预处理,例如归一化、序列化等操作%2.划分数据集为训练集和测试集%这里假设您将数据划分为train_data,train_labels,test_data,test_label......
  • 【独家首发】Matlab实现粒子群优化算法PSO优化Transformer-BiLSTM实现负荷数据回归预
    %假设您有负荷数据load_data和相应的回归标签regression_labels%1.数据预处理%在这一步中,您需要对负荷数据进行适当的预处理,例如归一化、序列化等操作%2.划分数据集为训练集和测试集%这里假设您将数据划分为train_data,train_labels,test_data,test_label......
  • 直播平台搭建,需要实现的核心要素之队列
    直播平台搭建,需要实现的核心要素之队列队列的实现在直播平台搭建中,队列的实现分为队列的定义和操作,如前所述,队列是元素的有序集合,添加操作发生在其尾部,移除操作则发生在头部。队列的操作顺序是先进先出(FIFO),它支持以下操作。Queue():创建一个空队列。它不需要参数,且会......
  • Kylin查询优化器深度解析:大数据查询性能的加速引擎
    Kylin查询优化器深度解析:大数据查询性能的加速引擎ApacheKylin是一个开源的分布式分析引擎,专为Hadoop和Spark平台上的大数据集提供快速的SQL查询能力。Kylin的核心优势之一是其强大的查询优化器,它能够智能地优化查询计划,显著提高查询性能。本文将深入探讨Kylin的查询优化......
  • 斜率优化
    斜率优化[HNOI2008]玩具装箱状态转移方程:设A为\(sum_i+i\),B为\(sum_j+j+L+1\)简化可得:\[f_i=min(f_i,f_j+A^2-2AB+B^2)\]稍微分解一下,有:\[f_i=f_j+A^2-2AB+B^2\\f_j+B^2=2AB+f_i-A^2\]设\(f_j+B^2\)为点\(y\),\(2A\)为\(k\),\(B\)为\(x\),\(f_i-A^2\)为\(b......
  • 从零开始使用GPT-4o mini:配置、微调与优化
    引言随着人工智能技术的不断发展,OpenAI推出的GPT-4omini模型吸引了众多开发者的关注。作为一种更经济实惠且高效的语言模型,GPT-4omini在多模态推理和成本效益方面表现出色。本篇文章旨在分享使用GPT-4omini的经验,从初始设置到性能优化,涵盖各个应用场景,并提供实际的开发建议......
  • DAY10 栈与队列part01
     理论基础文章讲解:https://programmercarl.com/%E6%A0%88%E4%B8%8E%E9%98%9F%E5%88%97%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html232.用栈实现队列 注意为什么要用两个栈题目链接/文章讲解/视频讲解:https://programmercarl.com/0232.%E7%94%A8%E6%A0%88%E5%AE%9E%E7%8E%......
  • C++优先队列 涵盖各种易错,技巧,应用和原理(附手写代码)
    当然也可以不看==> 阅读我的文章前请务必先阅读此文章! 都是废话这个文章里有视频,不知道为什么一点开文章就会播放,可以先翻到最后暂停一下再回来看目录阅读文章须知引言优先队列的概念优先队列的创建优先队列的操作*还有一些不常用的:优先队列的技巧如果类型是结构......
  • 服务器性能优化文档
    服务器性能优化文档1.概述本文档主要针对服务器性能优化方案进行介绍,旨在通过合理的配置和优化,提升服务器性能,降低资源占用,提高用户体验。2.性能问题分析CPU占用率过高:可能是系统进程过多、程序存在性能瓶颈、恶意攻击等原因导致。内存使用率过高:可能是程序内存泄漏、缓......