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

高并发秒杀三大核心技术实战

在构建高并发秒杀系统时,确保系统在高流量冲击下仍能保持高性能、高可用和数据一致性是核心目标。经过对业界主流方案的梳理,可以提炼出三大核心技术支柱:原子性库存扣减、分布式锁防超卖、以及异步消息队列解耦。下面将结合具体技术实现和实战代码,详细解析这三大技术。

1. 原子性库存扣减:Redis + Lua脚本

秒杀的核心挑战之一是库存的精确扣减。在超高并发下,简单的“查询-判断-更新”数据库操作会产生严重的超卖问题。使用Redis配合Lua脚本是确保库存扣减原子性的黄金标准

  • 原理:Lua脚本在Redis中执行时是原子的,这意味着脚本执行期间不会被其他命令插入,从而避免了并发竞争。
  • 优势:将多个操作(如:检查库存、扣减库存、记录用户购买资格)打包成一个原子操作,性能极高(微秒级),且逻辑清晰。

实战代码示例

-- 扣减库存的Lua脚本 -- KEYS[1]: 商品库存键,如 `seckill:stock:1001` -- ARGV[1]: 要扣减的数量,通常为1 -- 返回值: 剩余库存,若为-1表示库存不足,-2表示重复扣减(可选逻辑) local stockKey = KEYS[1] local change = tonumber(ARGV[1]) -- 获取当前库存 local currentStock = tonumber(redis.call('GET', stockKey)) if currentStock == nil then -- 库存键不存在,可能是初始化问题 return -3 end if currentStock < change then -- 库存不足 return -1 end -- 扣减库存 local newStock = redis.call('DECRBY', stockKey, change) return newStock
// Java中调用上述Lua脚本 @Component public class SeckillService { @Autowired private StringRedisTemplate redisTemplate; private static final DefaultRedisScript<Long> SECKILL_SCRIPT; static { SECKILL_SCRIPT = new DefaultRedisScript<>(); SECKILL_SCRIPT.setLocation(new ClassPathResource("scripts/seckill.lua")); SECKILL_SCRIPT.setResultType(Long.class); } public Long handleSeckill(Long productId) { String stockKey = "seckill:stock:" + productId; // 原子执行Lua脚本扣减库存 Long result = redisTemplate.execute( SECKILL_SCRIPT, Collections.singletonList(stockKey), "1" // 扣减数量 ); if (result != null && result >= 0) { // 扣减成功,进入后续订单流程 return result; // 返回剩余库存 } else if (result == -1) { // 库存不足 throw new RuntimeException("商品已售罄"); } // 其他错误处理 throw new RuntimeException("秒杀失败"); } }

2. 分布式锁防超卖:Redisson可重入锁

即便库存扣减是原子的,仍需防止同一用户在极端时间内重复提交,或确保如“一人一单”等业务规则的互斥执行。分布式锁是解决分布式环境下互斥访问的关键。

  • 选型:相比自己基于SETNX实现,更推荐使用Redisson客户端,它提供了成熟的、可重入的分布式锁实现,并解决了锁的自动续期和释放问题。
  • 场景:用于在创建订单等关键链路上进行串行化控制。

实战代码示例

// 使用Redisson实现分布式锁,确保“一人一单” @Service public class OrderService { @Autowired private RedissonClient redissonClient; public String createOrder(Long userId, Long productId) { String lockKey = "seckill:order:lock:" + productId + ":" + userId; RLock lock = redissonClient.getLock(lockKey); try { // 尝试加锁,最多等待100毫秒,锁持有时间10秒 boolean isLocked = lock.tryLock(100, 10000, TimeUnit.MILLISECONDS); if (!isLocked) { throw new RuntimeException("系统繁忙,请稍后再试"); } // 锁内执行核心业务:检查是否已下单、创建订单等 // ... 业务逻辑,例如查询数据库判断用户是否已有订单 return doCreateOrder(userId, productId); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("锁定异常", e); } finally { // 无论如何,最终必须释放锁 if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } private String doCreateOrder(Long userId, Long productId) { // 实际创建订单的数据库操作 // ... 省略数据库插入等逻辑 return "订单创建成功,订单号:" + generateOrderNo(); } }

3. 异步消息队列解耦:Redis Stream / RabbitMQ

秒杀请求峰值极高,但创建订单、扣减数据库库存、发送通知等操作是耗时的I/O操作。异步消息队列能将瞬时高并发请求转换为平稳的异步任务流,实现流量削峰和系统解耦。

  • 两种主流选择
    1. Redis Stream:轻量级,无需额外中间件,适合数据量不大、对消息持久化要求不是极端高的场景。
    2. RabbitMQ:功能强大的专业消息队列,支持多种协议、高可靠性和复杂的路由策略。

技术对比与选择

特性Redis StreamRabbitMQ
部署复杂度低(与Redis一体)中(需独立部署)
功能丰富度基础消息队列功能丰富(多种交换机、路由、ACK机制)
可靠性依赖Redis持久化非常高(持久化、确认、事务)
适用场景轻量级解耦、实时流处理复杂业务解耦、高可靠异步任务

实战代码示例 (Redis Stream)

// 生产者:秒杀成功后,将订单任务放入Stream @Service public class SeckillProducerService { @Autowired private StringRedisTemplate redisTemplate; public void produceOrderTask(SeckillMessage message) { String streamKey = "stream:seckill:orders"; Map<String, String> messageBody = new HashMap<>(); messageBody.put("userId", message.getUserId().toString()); messageBody.put("productId", message.getProductId().toString()); messageBody.put("seckillId", message.getSeckillId().toString()); // 将消息添加到Stream RecordId recordId = redisTemplate.opsForStream().add(streamKey, messageBody); System.out.println("生产消息成功,ID: " + recordId); } } // 消费者:从Stream消费并处理订单 @Component public class SeckillConsumerService { @Autowired private StringRedisTemplate redisTemplate; @Autowired private OrderService orderService; @PostConstruct public void consumeOrderTask() { String streamKey = "stream:seckill:orders"; String consumerGroup = "order-group"; String consumerName = "consumer-1"; // 创建消费者组(如果不存在) try { redisTemplate.opsForStream().createGroup(streamKey, consumerGroup); } catch (Exception e) { // 组可能已存在 } while (true) { // 从消费者组读取待处理消息 List<MapRecord<String, Object, Object>> records = redisTemplate.opsForStream().read( Consumer.from(consumerGroup, consumerName), StreamReadOptions.empty().count(10).block(Duration.ofSeconds(2)), StreamOffset.create(streamKey, ReadOffset.lastConsumed()) ); if (records != null && !records.isEmpty()) { for (MapRecord<String, Object, Object> record : records) { Map<Object, Object> value = record.getValue(); // 处理订单业务 Long userId = Long.valueOf((String)value.get("userId")); Long productId = Long.valueOf((String)value.get("productId")); try { orderService.asyncCreateOrder(userId, productId); // 处理成功,确认消息 (ACK) redisTemplate.opsForStream().acknowledge(streamKey, consumerGroup, record.getId()); } catch (Exception e) { // 处理失败,可放入死信队列或记录日志 System.err.println("处理订单失败: " + record.getId()); } } } } } }

实战代码示例 (RabbitMQ)

// 使用RabbitMQ进行异步订单处理 @Component public class RabbitMQOrderProducer { @Autowired private RabbitTemplate rabbitTemplate; public void sendOrderMessage(SeckillMessage message) { // 发送消息到秒杀订单队列 rabbitTemplate.convertAndSend("seckill.order.exchange", "seckill.order.routing.key", message); System.out.println(" [x] Sent order message for user: " + message.getUserId()); } } @Service public class RabbitMQOrderConsumer { @RabbitListener(queues = "seckill.order.queue") public void receiveOrderMessage(SeckillMessage message) { System.out.println(" [x] Received order message for user: " + message.getUserId()); // 异步处理订单创建 // ... 调用OrderService } }

总结:三大技术的协同工作流

一个完整的秒杀请求处理流程,是三大核心技术协同工作的结果:

  1. 请求入口:用户发起秒杀请求。
  2. 原子扣减:服务端首先执行Redis Lua脚本,原子性地扣减缓存中的库存。若失败(库存不足),直接返回失败。
  3. 防重校验:扣减成功后,使用Redisson分布式锁,锁定“用户-商品”维度,执行“一人一单”等业务规则校验。
  4. 异步落库:校验通过后,将订单信息作为消息,发送至Redis Stream 或 RabbitMQ消息队列,立即返回用户“秒杀成功”提示。
  5. 最终消费:独立的消费者服务从消息队列中取出任务,异步、平稳地完成数据库订单创建、库存持久化扣减、发送短信通知等耗时操作。

通过以上组合,系统前端可以承受极高的QPS(每秒查询率),而将压力平滑地转移到后端异步处理,在保证数据强一致性的同时,极大地提升了系统的整体吞吐量和用户体验。


参考来源

  • Java高并发秒杀系统实战示例
  • 高并发环境下的UUID生成终极指南:ramsey/uuid原子操作深度解析
  • 秒杀系统设计三件套:Redis+Lua+Stream消息队列保姆级配置
  • 高并发商品秒杀系统Java实战代码设计
  • Java高并发秒杀系统完整项目实战代码
  • Java高并发处理核心技术详解:从理论到实战
http://www.jsqmd.com/news/1131830/

相关文章:

  • vsftpd 3.0.5 安全配置实战:5项关键设置加固FTP服务器
  • KubeFed v0.3.1 实战部署:2集群联邦配置与Nginx应用分发验证
  • 最小权限原则实战:从Linux进程到云原生的五层权限收缩
  • 小产和流产有什么区别?
  • 2026最新8款AI编程助手学生党平替实测合集
  • 【共创季稿事节】随机数生成器:Math.random() 的原理与应用
  • NVMe 2.0b 控制器架构解析:3种控制器类型与2种模型的核心差异
  • web第十一次作业
  • 基于51单片机指纹密码锁/指纹解锁/指纹识别门禁系统/电子21(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • 现代密码学实战:Python实现3种经典密码(凯撒、维吉尼亚、RSA)
  • SmileCli04 Multi_Agent实现
  • 如何轻松获取大疆无人机历史固件:5个简单步骤实现固件自由
  • OpenCV 4.x 形态学操作实战:3种结构元素与5种算子对字符识别效果对比
  • 2026最新5款AI编程工具平替实测合集|开发者全方位权威榜单
  • C++中内存池的简单原理及实现详解
  • 告别AI能力局限:从零读懂Tool Calling,实现大模型调用外部工具、落地真实业务
  • PCIe 6.0 DMWr 实战:3步配置与 64B/128B 负载性能对比
  • OnlyOffice 编译包跨平台部署:CentOS 7 系统 5 项关键服务配置详解
  • 今天我重读了一次《重构》,说说我觉得能在AI中用到的几个地方
  • 5分钟解锁:FGA如何让你每天从FGO刷本中解放3小时
  • 河南洛阳无人机维修机构推荐|河南筋斗云翼航空一站式低空产业实训基地
  • 首月半价cursor
  • 26-MCP协议是什么
  • Plone安全架构解析:默认拒绝与五维控制的开源实践
  • 深度学习过拟合实战:L1/L2正则化与Dropout在Auto MPG回归任务中的5方案对比
  • VOC、COCO、YOLO 3 种目标检测数据集格式对比与 Python 转换脚本
  • mba法律论文选题
  • NAND Flash 3D/2D 架构演进:从平面到 200+ 层堆叠的容量与性能跃迁
  • UE4 UMG 3D模型显示性能对比:RenderTarget 3种分辨率与2种渲染模式实测
  • (5,2)线性分组码标准阵列译码原理与Python仿真实现【P124302018-王开源,P124302045-张俊豪,P124302003-李则翰,P124302048-张子璇】