• 仿黑马点评-redis整合【四 优惠卷秒杀(上) 】


    前言
    👏作者简介:我是笑霸final,一名热爱技术的在校学生。
    📝个人主页:个人主页1 || 笑霸final的主页2
    📕系列专栏:《项目专栏》
    📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
    🔥如果感觉博主的文章还不错的话,👍点赞👍 + 👀关注👀 + 🤏收藏🤏

    在这里插入图片描述

    🐉介绍🐉

    在这里插入图片描述

    🐉全局唯一ID🐉

    我们使用全局id生成器
    在这里插入图片描述
    我们能不能用redis来完成这个任务?
    在这里插入图片描述
    代码

    // 1. 生成时间戳//2.生成序列号//3.拼接并返回
    获取时间戳

      public static void main(String[] args) {
            //获取2022年 1月 1号 0点的秒数
            LocalDateTime time = LocalDateTime.of(2022, 1, 1, 0, 0);
            long second = time.toEpochSecond(ZoneOffset.UTC);
            log.info(Long.toString(second));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public Long nexId(String keyPrefix){
           // 1. 生成时间戳 31位数字 单位秒
            LocalDateTime now = LocalDateTime.now();
            long nowSecond = now.toEpochSecond(ZoneOffset.UTC);//当前秒数
            Long timestamp=nowSecond- BEG_TIMESTAMP;
    
            //2.生成序列号
            //2.1获取当前日期
            String yyyyMMdd = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
            //2.2自增长
            Long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + yyyyMMdd);
            //3.拼接并返回
    
            return timestamp<<32 | count;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

    🐉添加优惠卷🐉

    背景:每个商代都可以发布优惠卷,评价卷都可以任意购买,优惠卷需要秒杀抢购!!

    数据库

    • 平价卷在这里插入图片描述
    • 秒杀卷
      在这里插入图片描述

    🐉新增优惠卷json数据

    url地址:http://localhost:8081/voucher/seckill

    {
        "shopId":1,
        "title":"100元代金卷",
        "subTitle":"周一到周五可用",
        "rules":"测试",
        "payValue":8000,
        "actualValue":10000,
        "type":1,
        "stock":100,
        "beginTime":"2022-01-25T10:09:17",
        "endTime":"2023-01-25T10:09:17",
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    🐉实现优惠卷秒杀下单(基本下单)🐉

    在这里插入图片描述

    一般流程

    在这里插入图片描述

    • 判断秒杀是否开始,或者结束
    • 库存是否充足

    代码
    在这里插入图片描述

    @Service
    public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
        @Resource
        private ISeckillVoucherService seckillVoucherService;
    
        @Resource
        private RedisIdWorker redisIdWorker;
    
        @Override
        @Transactional//两张表 加上事务
        public Result seckillVoucher(Long voucherId) {
    
            //1.查询id
            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("库存不足");
            }
            //5.扣减库存
            boolean success= seckillVoucherService
                    .update()
                    .setSql("stock=stock-1")
                    .eq("voucher_id",voucherId).update();
            if (!success){
                //库存不足
                return Result.fail("库存不足");
            }
            //6.创建订单
            VoucherOrder voucherOrder = new VoucherOrder();
            //6.1订单id 用id全局唯一生成器
            Long orderId = redisIdWorker.nexId("order");
            voucherOrder.setId(orderId);
            //6.2用户id
            Long userId = UserHolder.getUser().getId();
            voucherOrder.setUserId(userId);
            //6.3代金卷id
            voucherOrder.setVoucherId(voucherId);
            save(voucherOrder);
            //7.返回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
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    🐉实现优惠卷秒杀下单(超卖问题)🐉

    解决超卖问题可以加锁:

    • 乐观锁:认为线程安全问题不一定会发生,因此不加锁,只是在更新数据时去判断有没有其它线程对数据做了修改。如果没有修改则认为是安全的,自己才更新数据。如果已经被其它线程修改说明发生了安全问题,此时可以重试或异常。
    • 悲观锁:认为线程安全问题一定会发生,因此在操作数据之前先获取锁,确保线程串行执行。例如Synchronized、Lock都属于悲观锁

    在这里插入图片描述

    乐观锁实现方案

    1.版本号法(运用最广泛的):每次更新数据库的时候按照版本查询,并且要更新版本。

    2.CAS
    CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。
    CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。
    更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。
    在这里插入图片描述

    CAS法代码:

    @Service
    public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
        @Resource
        private ISeckillVoucherService seckillVoucherService;
    
        @Resource
        private RedisIdWorker redisIdWorker;
    
        @Override
        @Transactional//两张表 加上事务
        public Result seckillVoucher(Long voucherId) {
    
            //1.查询id
            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("库存不足");
            }
            //5.扣减库存 乐观锁 在这一刻去判断更新时和查询到的库存是否一致
                //        boolean success= seckillVoucherService
                //                .update()
                //                .setSql("stock=stock-1")
                //                .eq("voucher_id",voucherId).update();
            boolean success= seckillVoucherService
                    .update()
                    .setSql("stock=stock-1")
                    .eq("voucher_id",voucherId)
                    .eq("stock",voucher.getStock())
                    .update();
            if (!success){
                //库存不足
                return Result.fail("库存不足");
            }
            //6.创建订单
            VoucherOrder voucherOrder = new VoucherOrder();
            //6.1订单id 用id全局唯一生成器
            Long orderId = redisIdWorker.nexId("order");
            voucherOrder.setId(orderId);
            //6.2用户id
            Long userId = UserHolder.getUser().getId();
            voucherOrder.setUserId(userId);
            //6.3代金卷id
            voucherOrder.setVoucherId(voucherId);
            save(voucherOrder);
            //7.返回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
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    弊端:成功率太低:
    改进一下
    判断库存和0的大小
    在这里插入图片描述

    🐉实现优惠卷秒杀下单(一人一单)🐉

    在这里插入图片描述

    @Service
    public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
        @Resource
        private ISeckillVoucherService seckillVoucherService;
    
        @Resource
        private RedisIdWorker redisIdWorker;
    
        @Override
    
        public Result seckillVoucher(Long voucherId) {
    
            //1.查询id
            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();
            //intern()在常量池里先去找一样的地址返回
            synchronized(userId.toString().intern()) {
                VoucherOrderServiceImpl proxy 
                        =(VoucherOrderServiceImpl)AopContext.currentProxy();//代理对象
                //因为没有加事务 事务用的代理对象 可能存在事务失效
                //没有用代理对象默认是 this.creteVoucherOrder(voucherId)
                return proxy.creteVoucherOrder(voucherId);
            }
        }
    
        @Transactional//两张表 加上事务
        public  Result creteVoucherOrder(Long voucherId) {
            //5一人一单
    
            Long userId = UserHolder.getUser().getId();
        
                //5.1查询订单
                Integer count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
    
                //5.2查询是否存在
                if (count > 0) {
                    //存在就不能下单了
                    return Result.fail("你已经购买过了,只能买一次哟");
                }
                //6.扣减库存 乐观锁 在这一刻去判断更新时和查询到的库存是否一致
                //        boolean success= seckillVoucherService
                //                .update()
                //                .setSql("stock=stock-1")
                //                .eq("voucher_id",voucherId).update();
                boolean success = seckillVoucherService
                        .update()
                        .setSql("stock=stock-1")
                        .eq("voucher_id", voucherId)
    //                .eq("stock",voucher.getStock())
                        .gt("stock", 0)//判断 stock>0可行
                        .update();
                if (!success) {
                    //库存不足
                    return Result.fail("库存不足");
                }
                //7.创建订单
                VoucherOrder voucherOrder = new VoucherOrder();
                //7.1订单id 用id全局唯一生成器
                Long orderId = redisIdWorker.nexId("order");
                voucherOrder.setId(orderId);
                //7.2用户id
    
                voucherOrder.setUserId(userId);
                //7.3代金卷id
                voucherOrder.setVoucherId(voucherId);
                save(voucherOrder);
                //8.返回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
    • 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
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    在这里插入图片描述
    需要加上

     		<dependency>
                <groupId>org.aspectjgroupId>
                <artifactId>aspectjweaverartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4

    同时启动类也加上@EnableAspectJAutoProxy(exposeProxy = true)//暴露代理对象

  • 相关阅读:
    Java的基础知识
    利用Elasticsearch提升Java应用的搜索能力
    nvme安装
    11+孟德尔随机化+GWAS分析
    matlab数据处理: cell table array+datetime
    可能是2022最详细的【Java SE 】顺序结构、分支结构、循环结构讲解
    动态分区算法
    读博后才知道的真道理
    ref 操作 React 定时器
    python利用read_table读取dat文件案例详解
  • 原文地址:https://blog.csdn.net/weixin_52062043/article/details/127764631