当前位置: 首页 > news >正文

黑马点评-超卖问题

什么是超卖问题?

想象一个场景:
黑马点评搞秒杀活动,某商品库存 100 件。活动开始后,15 个用户同时下单,最后系统显示卖出了 101 件,库存变成了 - 1 —— 这就是超卖。
在这里插入图片描述
超卖本质多线程环境下,库存扣减操作未正确同步,导致 实际销量 > 库存量的严重业务错误。针对这一问题常见解决方案就是加锁

为什么会超卖?
用代码模拟一下问题:

// 错误的扣减库存方式
public boolean seckill(Long voucherId) {// 1. 查询库存SeckillVoucher voucher = seckillVoucherService.getById(voucherId);int stock = voucher.getStock();// 2. 判断库存是否充足if (stock > 0) {// 3. 扣减库存(stock=stock-1)voucher.setStock(stock - 1);seckillVoucherService.updateById(voucher);}
}

问题出在 “查询→判断→扣减” 这三步不是原子操作:

  • 线程 1 查询到库存 = 1,还没扣减;
  • 线程 2 也查询到库存 = 1,也进入扣减逻辑;
  • 最后库存变成 - 1,超卖了。

解决方案

解决方案对比:悲观锁 vs 乐观锁

方案实现原理适用场景性能影响实现复杂度
悲观锁“先锁定再操作”(如synchronized高冲突场景,数据强一致性要求高较高(串行执行)
乐观锁“先操作再验证”(版本号/条件判断)低冲突场景,读多写少低(并行执行)
分布式锁Redis/ZooKeeper,全局锁控制分布式系统,高并发秒杀中(网络开销)

悲观锁实战

public synchronized boolean seckill(Long voucherId)  {// 同上:查询→判断→扣减
}

优点:简单,绝对不会超卖。
缺点:并发高时,大家都在等锁,响应慢,像排队结账排成长龙。

乐观锁实战:黑马点评解决方案

核心代码实现

public boolean seckill(Long voucherId) {// 1. 查询优惠券库存SeckillVoucher voucher = seckillVoucherService.getById(voucherId);// 2. 判断秒杀是否开始/结束if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {throw new RuntimeException("秒杀尚未开始!");}if (voucher.getEndTime().isBefore(LocalDateTime.now())) {throw new RuntimeException("秒杀已结束!");}// 3. 库存不足直接返回if (voucher.getStock() < 1) {return false;}// 4. 乐观锁扣减库存boolean success = seckillVoucherService.update().setSql("stock = stock - 1")  // SET stock = stock - 1.eq("voucher_id", voucherId)  // WHERE voucher_id = #{voucherId}.gt("stock", 0)               // AND stock > 0.update();return success;
}

方案优势

  • 无锁竞争:避免线程阻塞,提高并发能力
  • 轻量级:数据库级别实现,无需额外组件
  • 简单有效:SQL条件天然保证库存安全

潜在问题:高并发下的低成功率
当100线程同时抢10个商品时:

  • 乐观锁成功率 ≈ 10%
  • 90%请求失败需重试或放弃

进阶方案:Redis分布式锁

实现原理
在这里插入图片描述
核心代码:

public boolean seckillWithRedisLock(Long voucherId) {// 1. 获取分布式锁String lockKey = "lock:voucher:" + voucherId;String clientId = UUID.randomUUID().toString();try {// 尝试获取锁(设置10秒超时)Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS);if (!Boolean.TRUE.equals(locked)) {return false; // 获取锁失败}// 2. 执行库存扣减return doSeckill(voucherId);} finally {// 3. 释放锁(Lua脚本保证原子性)String script = "if redis.call('get', KEYS[1]) == ARGV[1] " +"then return redis.call('del', KEYS[1]) " +"else return 0 end";redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),Collections.singletonList(lockKey),clientId);}
}

性能优化:库存预减方案

架构设计
在这里插入图片描述

实现步骤
1. 预热库存到Redis:

redisTemplate.opsForValue().set("stock:voucher:"+voucherId, 100);

2. 预减库存:

Long stock = redisTemplate.opsForValue().decrement("stock:voucher:"+voucherId);
if (stock < 0) {// 库存不足,回滚redisTemplate.opsForValue().increment("stock:voucher:"+voucherId);return false;
}

3. 异步落库:

@Async
public void asyncUpdateStock(Long voucherId) {seckillVoucherService.update().setSql("stock = stock - 1").eq("voucher_id", voucherId).update();
}

方案选型指南

场景推荐方案QPS支持注意事项
低并发(<1000QPS)乐观锁1k-5k控制重试次数
中并发(5k-10kQPS)Redis分布式锁5k-10k设置锁超时
高并发(>10kQPS)库存预减+异步10k+保证最终一致性
超高并发Redis+Lua脚本50k+监控Redis负载

最佳实践总结
1. 多层防护:

  • 前端:按钮防重、验证码
  • 网关:请求限流
  • 服务:库存预减
  • 数据库:最终一致性
    2. 监控指标:
// 监控关键指标
meterRegistry.counter("seckill.requests").increment();
meterRegistry.counter("seckill.success").increment();
meterRegistry.counter("seckill.failures").increment();

3. 回退机制:

// 库存回补
if (updateFailed) {redisTemplate.opsForValue().increment("stock:voucher:"+voucherId);
}

超卖问题本质是并发控制的艺术,选择合适方案需平衡性能、一致性和复杂度。建议从乐观锁起步,随业务增长逐步升级到分布式方案!

http://www.dtcms.com/a/306579.html

相关文章:

  • 实现了加载 正向 碰撞 雅可比 仿真
  • 4.Origin2021如何绘制多组误差棒图?
  • Vulnhub靶场:thales
  • c++之基础B(第一课)
  • 力扣面试150(44/150)
  • Linux 系统进程管理与计划任务详解
  • Kafka 单机多 Broker 实例集群搭建 | 详情
  • 育儿补贴之外,父母更需要的 “认知补贴”|创客匠人
  • 测试用例颗粒度全解析
  • 领域驱动设计(DDD)在分布式系统中的架构实践
  • Linux:haproxy
  • ORACLE的表维护
  • Ubuntu LNMP
  • 容器化与Docker核心原理
  • 流程制造的数字孪生:从黑箱生产到全息掌控
  • 滚珠导轨在电子制造中的流畅性优势
  • 虚实共生的智能革命:元宇宙、物联网与 AI 融合生态全景图谱
  • 高可靠液晶屏系统解决方案深度解析
  • 基于 Python 开发的信阳市天气数据可视化系统源代码+数据库+课程报告
  • 老旧泵房物联网改造方案与成本效益深度解析
  • C++线程详解
  • 关于“LoggerFactory is not a Logback LoggerContext but Logback is on ......“的解决方案
  • [源力觉醒 创作者计划]_巅峰对话:文心 vs. DeepSeek/Qwen 3.0 深度解析
  • Wan2.2 - 阿里最新开源视频生成模型 支持文生视频/图生视频 支持50系显卡 一键整合包
  • 293F细胞是什么?
  • C语言《智能自平衡小车,实现平衡功能的基础上,加入了超声波避障、超声波跟随、蓝牙遥控等功能》+源代码+文档说明
  • DEC 指令
  • spark的broadcast variables
  • 重庆邮电大学2026年计算机/软件/人工智能/网安考研备考指南
  • css初学者第二天