秒杀系统是典型的高并发、短时流量洪峰场景。用户可能在1秒内涌入百万次请求,但库存可能只有100件商品。如何保证系统不崩溃、不超卖、不卡顿?本文将结合实战经验,梳理秒杀系统的核心设计原则和关键技术方案。
一、为什么秒杀系统难?
假设一场秒杀活动:1000件商品,10万人同时抢购。
系统可能面临:
- 数据库击穿:每秒数万次查询直接打垮MySQL。
- 超卖问题:并发扣减库存导致实际卖出数量超过库存。
- 服务雪崩:某个节点崩溃后,引发连锁反应。
设计目标:
✅ 不超卖
✅ 高可用(99.99%可用性)
✅ 低延迟(用户秒级响应)
二、分层拦截:90%的流量不进后端
1. 前端优化:拦截无效请求
-
静态资源CDN化
商品详情页提前渲染为静态HTML,通过CDN分发,减少90%动态请求。<!-- 静态页面示例:直接展示倒计时,不请求服务端 --> <div class="countdown" data-start="2024-06-01 20:00:00"></div>运行 HTML
-
按钮防重复点击
点击后禁用按钮,防止用户疯狂连点。document.getElementById("buyButton").onclick = function() { this.disabled = true; // 禁用按钮 submitOrder(); }; -
答题验证
引入滑块/算术题验证,拦截机器人请求(类似12306)。
2. 网关层:限流与过滤
- 全局限流:Nginx限制每秒最大请求数(如1万QPS)。
- 用户级限流:同一用户5秒内只能请求一次(Redis记录状态)。
- 黑名单机制:过滤异常IP、恶意UA(如爬虫特征)。
三、库存扣减:如何保证不超卖?
1. Redis原子化扣减
-
预热库存:提前将库存加载到Redis集群。
SET stock_sku_1001 1000 # 初始化库存 -
原子操作:使用
DECR命令扣减库存(无需事务)。Long remain = redisTemplate.opsForValue().decrement("stock_sku_1001"); if (remain >= 0) { // 扣减成功,生成订单 } else { // 库存不足 } -
Lua脚本兜底(应对极端并发):
-- 查询库存并扣减(原子化) local stock = redis.call('GET', KEYS[1]) if stock and tonumber(stock) > 0 then redis.call('DECR', KEYS[1]) return 1 -- 成功 end return 0 -- 失败
2. 库存分段:降低热点Key压力
将1000库存拆分为10个Key(如stock_sku_1001_1到stock_sku_1001_10),每个存100件。用户随机选择一个Key扣减,分散竞争。
四、异步化处理:订单与支付解耦
1. 消息队列削峰
-
抢购成功→发MQ→异步下单
用户抢到资格后,立即返回“排队中”,同时发送消息到RocketMQ/Kafka。// 发送MQ消息 SendResult result = producer.send(new Message("order_topic", "下单数据")); -
订单服务消费消息:
异步写入MySQL,避免瞬时数据库压力(从1万QPS降到100 QPS)。
2. 订单状态管理
- 待支付状态:订单保留15分钟,超时释放库存(定时任务回补Redis)。
- 支付回调:用户支付后更新订单状态,扣减最终库存。
五、容灾设计:如何应对突发故障?
1. 熔断降级
- 监控系统负载(如CPU > 80%),自动触发限流策略。
- 兜底页面:直接返回“活动太火爆,请稍后再试”。
2. Redis故障降级
- 降级到数据库:使用数据库行锁(
SELECT ... FOR UPDATE)扣减库存。 - 分布式锁:通过ZooKeeper/Redisson加锁,防止超卖(性能较低,慎用)。
六、数据一致性保障
1. 最终一致性
- Binlog监听:通过Canal监听MySQL变更,回滚超时未支付订单的库存。
- 对账任务:每小时对比Redis库存与DB订单总数,自动修复差异。
2. 数据库优化
-
分库分表:订单表按用户ID分片(如user_id % 16)。
-
热点更新:
UPDATE product SET stock = stock - 1 WHERE id = 1001 AND stock > 0;
七、实战建议:压测与演练
1. 全链路压测
- 模拟真实流量(如10倍峰值),验证限流、降级、库存回补逻辑。
- 工具:JMeter、阿里云PTS。
2. 混沌工程
- 随机杀死节点、模拟网络延迟,验证系统自愈能力。
总结:秒杀系统设计原则
| 核心原则 | 关键技术实现 |
|---|---|
| 分层拦截 | 前端静态化 + 网关限流 |
| 异步削峰 | MQ异步下单 + 订单状态机 |
| 数据强一致 | Redis原子扣减 + 最终一致性对账 |
| 容灾兜底 | 熔断降级 + 数据库降级方案 |
通过以上方案,系统可支撑百万级QPS,同时保证不超卖、低延迟和高可用。实际项目中需结合业务需求调整(如是否允许排队、部分成功),但核心思路始终是:层层过滤、异步解耦、数据兜底。