☰
SpringBoot实战:从0到1搭建高并发秒杀系统
2026/9/27 22:59:28 网站建设 项目流程

数据库设计:别让库存字段成为瓶颈

商品表里库存字段用int,扣减时update stock = stock - 1 where id = ? and stock > 0,利用数据库行锁保证不超卖。但这条SQL在并发下会串行执行,QPS上限就是单行锁的吞吐量,撑死几百。所以数据库只做最终扣减,不做第一道防线。订单表用唯一索引(user_id, goods_id)防止同一用户重复下单,插入时捕获唯一键冲突即可。

Redis预减库存:把战场前移

秒杀开始前,把商品库存加载到Redis。用DECR命令原子递减,返回值小于0说明库存不足,直接拦截。这一步把绝大多数无效请求挡在数据库之外。但要注意:Redis扣减成功不代表最终能下单,所以需要记录一条“已扣减但未支付”的流水,后续用消息队列异步落库。如果用户超时未支付,再把库存加回去。

消息队列:削峰填谷的关键

Redis扣减成功后,不直接写数据库,而是发一条消息到RocketMQ或Kafka。消费者端控制并发数,比如开20个线程慢慢消费,数据库压力就平稳了。消息里带用户ID、商品ID、扣减数量。消费失败要有重试机制,重试多次仍失败则回滚Redis库存并记录异常。这样即使数据库短暂抖动,也不会丢单。

限流与防刷:把无效流量挡在门外

网关层用Sentinel或Redis+Lua做令牌桶限流,比如每秒放行5000个请求。用户维度限流:同一用户每秒最多请求一次,用Redis的SETNX加过期时间实现。商品维度限流:每个商品每秒放行固定数量。前端按钮点击后置灰,防止重复提交。验证码是最后一道防线,但会牺牲体验,只在极端场景启用。

分布式锁:解决超卖的最后保障

如果Redis集群出现故障导致扣减不准,数据库层还有兜底。用Redisson的分布式锁,以商品ID为key,扣减前先获取锁,拿到锁后再次检查库存。但锁的粒度要细,只锁单个商品,不要锁整个秒杀活动。锁的超时时间设短一点,比如3秒,避免死锁。

前端优化:减少无效请求

静态资源全部走CDN,秒杀页面提前推送到边缘节点。接口地址不要写死在页面里,秒杀开始前通过Ajax获取动态URL,防止被脚本提前刷。倒计时用服务器时间,避免本地时间篡改。提交订单后轮询结果,不要一直转圈。

整体流程串起来

用户点击秒杀,先过网关限流,再校验用户资格,然后Redis预减库存。扣减成功发消息到MQ,返回“排队中”。消费者从MQ取消息,写订单表,扣数据库库存。用户轮询订单状态,成功则跳转支付。超时未支付,定时任务回滚Redis和数据库库存。

这套架构的核心思想是分层过滤:前端挡掉重复点击,网关挡掉超频请求,Redis挡掉超卖,MQ削平数据库压力,数据库做最终一致性。每层只做自己擅长的事,不要试图用一把锤子解决所有问题。SpringBoot的自动配置让我们能快速集成Redis、RocketMQ、Sentinel,但集成只是开始,压测和调优才是重头戏。用JMeter模拟一万并发,观察各层瓶颈,逐步调整线程池、连接池、超时时间,才能让系统真正扛住洪峰。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询