常见系统设计
秒杀系统
- 前端层:
静态资源缓存:通过CDN缓存商品图片、页面静态HTML,减少回源请求。
请求合并:合并用户频繁刷新的请求(如10秒内仅允许一次真实请求)。
端侧限流:通过JS或APP端限制用户高频点击(如倒计时按钮禁用、点击后置灰)。 - 接入层:
负载均衡:使用LVS/Nginx实现流量分发,结合IP Hash或一致性哈希减少后端压力。
请求队列:引入Kafka/RocketMQ缓冲瞬时流量(削峰填谷),按后端处理能力消费。
边缘计算:在CDN边缘节点拦截非法请求(如未登录用户、IP黑名单)。 - 服务层:
服务拆分:将核心功能(库存扣减、订单创建)与非核心功能(日志、通知)解耦。
读写分离:读服务(查询库存)与写服务(扣库存)独立部署,避免相互影响。
热点隔离:为秒杀商品分配独立资源池(专用集群、线程池、数据库连接池)。 - 存储层:
缓存抗压:库存预加载至Redis集群(使用Lua脚本实现原子化扣减)。
分库分表:订单库按用户ID分片,库存库按商品ID分片,降低单点压力。
最终一致:通过MQ异步同步缓存与数据库,允许短暂不一致但最终收敛。
问题处理
- 超卖
- 预扣库存:采用预扣除而非实时扣减(例如Redis原子化操作lua)
local stock = tonumber(redis.call('GET', KEYS[1]))
if stock > 0 thenredis.call('DECR', KEYS[1])return 1 -- 扣减成功
elsereturn 0 -- 库存不足
end
//库存要>0
UPDATE stock SET quantity = quantity - 1
WHERE item_id = #{itemId} AND quantity >= 1;
-
热点数据竞争
缓存预热:在秒杀开始前将库存数据加载到Redis并禁止直接访问DB。
Key分片:将热点商品的库存拆分为多个Key(如item_stock_1, item_stock_2),分散访问压力。
本地缓存+延迟双删:在服务节点本地缓存库存数据,结合延迟删除策略减少Redis访问。 -
异步订单处理
扣库存成功 → 发送消息到MQ → 订单服务消费并生成预订单。
支付服务异步回调确认,更新订单状态并扣减数据库库存。