核心内容摘要
2023家庭摄像头夫妻
今天我就把压箱底的库存抢购防超卖方案分享给你从原理到实战保证说得明明白白就算是刚入行的同学也能听懂。
先搞懂为什么会出现超卖少买库存抢购看似简单实则藏着不少坑并发请求量大秒杀活动瞬间可能有10万QPS数据库根本扛不住读取库存延迟用户看到的库存和实际库存不同步更新库存冲突多个用户同时抢购最后一件商品业务逻辑漏洞比如先创建订单后扣减库存网络延迟请求到达顺序和用户操作顺序不一致举个例子库存剩1件用户A和用户B同时抢购。
数据库先收到A的请求查询库存还有1件准备扣减同时B的请求也来了查询库存还是1件。
结果就是A和B都成功下单库存变成-1超卖了
架构设计多层防护才能稳如泰山解决超卖问题靠单一方法肯定不行必须上「组合拳」
前端层过滤无效请求限流限制同一用户单位时间内的请求次数防重复提交禁止短时间内重复点击倒计时同步确保所有用户看到的倒计时一致静态资源缓存减少服务器压力// 前端防重复提交示例 let isSubmitting false; document.getElementById(buyBtn).addEventListener(click, function() { if (isSubmitting) return; isSubmitting true; this.disabled true; this.innerHTML 抢购中...; // 发送请求 fetch(/api/seckill, { method: POST, body: JSON.stringify({ productId: 123 }) }).then(response { // 处理响应 }).finally(() { isSubmitting false; this.disabled false; this.innerHTML 立即抢购; }); });
后端层核心业务逻辑防护请求队列使用Redis或Kafka将请求排队依次处理库存预扣减先扣减库存再创建订单事务隔离确保库存查询和扣减在同一事务中库存校验创建订单前再次校验库存
数据层最终保障数据库锁使用悲观锁或乐观锁库存预热将库存加载到Redis中Redis原子操作使用Lua脚本确保库存扣减的原子性
核心技术点解决超卖的3大杀器
乐观锁高并发场景的首选乐观锁假设不会发生冲突只有在提交时才检查冲突。
适合读多写少的场景。
实现方式给库存表加一个版本号字段-- 乐观锁更新库存 UPDATE product_stock SET stock stock - 1, version version 1 WHERE product_id 123 AND stock 0 AND version #{version};如果更新成功说明没有冲突如果更新失败说明有其他请求已经修改了库存需要重试或返回失败。
Redis原子操作高性能保障Redis的decr命令是原子的可以用来扣减库存// Redis扣减库存示例 Long stock redisTemplate.opsForValue().decr(product:stock:
; if (stock
{ // 库存扣减成功 return true; } else { // 库存不足回滚 redisTemplate.opsForValue().incr(product:stock:
; return false; }更安全的方式是使用Lua脚本确保库存检查和扣减在一个原子操作中完成-- Lua脚本扣减库存 local stock redis.call(get, KEYS[1]) if not stock or tonumber(stock) 0 then return 0 end redis.call(decr, KEYS[1]) return
队列削峰将并发请求串行化使用消息队列如Kafka、RocketMQ将抢购请求排队然后由消费者按顺序处理// 发送抢购请求到队列 kafkaTemplate.send(seckill_topic, new SeckillMessage(userId, productId)); // 消费者处理抢购请求 KafkaListener(topics seckill_topic) public void handleSeckill(SeckillMessage message) { // 扣减库存 boolean success seckillService.reduceStock(message.getProductId()); if (success) { // 创建订单 orderService.createOrder(message.getUserId(), message.getProductId()); } else { // 返回库存不足 notificationService.sendFailMessage(message.getUserId()); } }
架构演进从简单到复杂初级阶段只在数据库层面加锁性能低中级阶段引入Redis缓存库存提高读取性能高级阶段使用消息队列削峰保护数据库终极阶段多级缓存队列分布式锁支持百万级并发
实战经验这些坑你必须避开不要迷信强一致性强一致性会牺牲性能大部分场景最终一致性足够Redis不是万能的必须考虑Redis故障的情况有降级方案库存不能只存在缓存中必须同步到数据库确保数据持久化不要忽视超时问题设置合理的超时时间避免长时间阻塞防刷单是关键识别恶意请求比如同一IP多次抢购测试是王道使用压测工具模拟高并发场景提前发现问题
经典案例某电商平台的秒杀系统某头部电商平台的秒杀系统架构前端使用CDN缓存静态资源减少服务器压力接入层使用Nginx限流过滤无效请求应用层使用Redis预扣减库存Lua脚本确保原子性消息队列使用Kafka将请求串行化处理数据库使用分库分表提高并发处理能力建立了完善的监控体系实时监控库存和订单情况这套架构支持每秒10万的并发请求库存准确率100%从未发生过超卖问题。
结语