创世网站建设,托管型网站,楼盘网官网,建筑案例的网站有哪些方面库存超卖问题分析 库存超卖问题其本质就是多个线程操作共享数据产生的线程安全问题#xff0c;即当一个线程在执行操作共享数据的多条代码的过程中#xff0c;其他线程也参与了进来#xff0c;导致了线程安全问题的产生。例如#xff1a;线程1发送请求#xff0c;查询库存… 库存超卖问题分析 库存超卖问题其本质就是多个线程操作共享数据产生的线程安全问题即当一个线程在执行操作共享数据的多条代码的过程中其他线程也参与了进来导致了线程安全问题的产生。例如线程1发送请求查询库存发现库存大于1在还没来及扣除库存时线程2甚至线程3等发送请求发现这个数量也是大于1那么这多个线程都会去扣除库存最终多个线程都去扣除库存此时就会出现库存的超卖问题。 image-20230707172612925 多线程安全问题常见的解决方案就是加锁 悲观锁认为线程安全问题一定会发生因此在操作数据之前先获取锁确保线程串行执行synchronized、lock都属于悲观锁属于同步锁让线程串行执行优点是简单粗暴缺点是性能一般 乐观锁认为线程安全问题不一定会发生只是在更新数据时判断有没有其他线程对数据做了修改优点是性能好缺点是存在成功率问题 如果没有修改则认为是安全的该线程进行数据更新 如果已经被其他线程修改则发生了线程安全问题此时可以重试或异常 使用乐观锁解决库存超卖问题 乐观锁的关键是判断之前查询得到的数据是否被修改过常见的方式有两种 版本号法 在数据库等中设置一个版本号字段每次操作数据会对版本号进行加一操作当每次读取数据时读出版本号字段 在修改数据的时候需要判断读出的版本号和数据库是否一致如果一致则表明在自己操作该数据的过程中没有其他线程操作该数据如果版本号不一致则表明数据以及被别人修改过了。 image-20230710111424101 CAS 在扣减库存时将现在库存和之前查询的库存做对比如果一样则说明没有其他线程在中间修改过库存数据则认为是线程安全的如果不一样则说明有线程在中间修改过库存数据。 image-20230710143222845 乐观锁解决超卖问题 使用在扣减库存时将现在库存和之前查询的库存做对比如果一样则表明没有人在此中间修改过库存则认定线程安全扣除库存 Overridepublic Long seckillVoucher(Long voucherId) { // 查询秒杀优惠券信息 SeckillVoucher seckillVoucher seckillVoucherService.getById(voucherId); //判断秒杀是否开始和结束 LocalDateTime beginTime seckillVoucher.getBeginTime(); LocalDateTime endTime seckillVoucher.getEndTime(); //如果当前时间 在开始时间之后 再结束时间之前 则表明秒杀能进行 LocalDateTime localDateTime LocalDateTime.now(); if ( localDateTime.isBefore(beginTime) || localDateTime.isAfter(endTime) ){ return null; } //获取库存量 Integer stock seckillVoucher.getStock(); if (ObjectUtil.isNull(stock) || ObjectUtil.isNotNull(stock) stock.intValue() 0){ return null; } //扣减库存 将现在库存和之前查询的库存做对比如果一样则表明没有人在此中间修改过库存则认定线程安全扣除库存 boolean update seckillVoucherService.update( new LambdaUpdateWrapperSeckillVoucher().setSql(stock stock - 1).eq(SeckillVoucher::getVoucherId, voucherId) .eq(SeckillVoucher::getStock, stock) ); if (!update){ return null; } //创建订单 VoucherOrder voucherOrder new VoucherOrder(); //创建订单ID long orderId redisIdWorker.nextId(order); voucherOrder.setId(orderId); //获取用户ID Long id UserHolder.getUser().getId(); voucherOrder.setUserId(id); // 代金券id voucherOrder.setVoucherId(voucherId); this.save(voucherOrder); return orderId;} 该方式通过测试能够发现失败率很高这主要是由于当多个人都拿到库存时只有一个能够扣减成功其他的由于.eq(SeckillVoucher::getStock, stock)无法完成扣减。 其实在超卖问题中我们需要保证的是在没用库存的情况下不能再进行库存扣减所有需要保证的是库存大于0所有可以设置.gt(SeckillVoucher::getStock, 0) Overridepublic Long seckillVoucher(Long voucherId) { // 查询秒杀优惠券信息 SeckillVoucher seckillVoucher seckillVoucherService.getById(voucherId); //判断秒杀是否开始和结束 LocalDateTime beginTime seckillVoucher.getBeginTime(); LocalDateTime endTime seckillVoucher.getEndTime(); //如果当前时间 在开始时间之后 再结束时间之前 则表明秒杀能进行 LocalDateTime localDateTime LocalDateTime.now(); if ( localDateTime.isBefore(beginTime) || localDateTime.isAfter(endTime) ){ return null; } //获取库存量 Integer stock seckillVoucher.getStock(); if (ObjectUtil.isNull(stock) || ObjectUtil.isNotNull(stock) stock.intValue() 0){ return null; } //扣减库存 将现在库存和之前查询的库存做对比如果一样则表明没有人在此中间修改过库存则认定线程安全扣除库存 boolean update seckillVoucherService.update( new LambdaUpdateWrapperSeckillVoucher().setSql(stock stock - 1).eq(SeckillVoucher::getVoucherId, voucherId) .gt(SeckillVoucher::getStock, 0) ); if (!update){ return null; } //创建订单 VoucherOrder voucherOrder new VoucherOrder(); //创建订单ID long orderId redisIdWorker.nextId(order); voucherOrder.setId(orderId); //获取用户ID Long id UserHolder.getUser().getId(); voucherOrder.setUserId(id); // 代金券id voucherOrder.setVoucherId(voucherId); this.save(voucherOrder); return orderId;} 本文由 mdnice 多平台发布