秒杀怎么优化
最近面试一位资深开发,过程中他提到项目中有秒杀场景,但提到的优化手段仅是"加机器"和"放缓存"。这显然是不够的,说明对高并发系统的理解还不够深入。
本文结合极客时间课程《如何设计一个秒杀系统》,总结秒杀系统的核心优化思路和技术方案。
引言:理解秒杀场景的核心挑战
秒杀的本质是在极短时间内涌入海量请求,对有限的商品库存进行抢购。这带来了几个核心挑战:
- 高并发读:大量用户同时访问商品详情页。
- 高并发写:大量用户同时尝试下单、减库存。
- 瞬时流量峰值:流量不是平稳的,而是突发性的。
- 一致性要求:库存、订单等数据必须准确一致。
- 用户体验:需要在压力下尽可能保证部分用户成功。
我们的优化目标是:将绝大多数无效请求挡在系统外围,仅允许少量有效请求进入核心交易链路,并确保核心链路的高效与稳定。
一、架构设计的核心原则:"四要一不要"
这是通用的高并发系统设计准则,也适用于秒杀:
1. 数据要尽量少
- 目标:减少数据传输和处理开销。
- 实践:只返回必要字段;压缩响应;减少序列化/反序列化。
2. 请求数要尽量少
- 目标:降低连接和处理压力。
- 实践:批量接口、缓存兜底、减少调用链。
3. 路径要尽量短
- 目标:缩短响应链路,减少潜在故障点。
- 实践:关键链路直连服务,移除冗余逻辑和中间层。
4. 依赖要尽量少且分级
- 目标:避免雪崩效应。
- 实践:区分核心/非核心依赖,异步调用或降级非核心依赖,核心依赖多副本部署。
5. 不要有单点(SPOF)
- 目标:任何组件故障都不应影响整体服务。
- 实践:应用、数据库、缓存、MQ、负载均衡等都需高可用部署。
二、关键技术手段
1. 动静分离
- 静态数据:商品信息、活动规则使用 CDN 或缓存预热。
- 动态数据:如库存、下单状态,动态请求,严格限流保护。
关键:将"所有人共享的数据"与"用户个性化数据"分离处理。
2. 热点数据处理
- 热点商品预缓存:提前部署到 Redis 等缓存。
- 请求限量:使用 MQ、线程池限制同时处理数量。
- 三层隔离策略 :
- 业务隔离:商品预热、提前准备;
- 系统隔离:使用独立服务域名或服务集群;
- 数据隔离:独立缓存、独立数据库。
3. 流量控制与排队机制
- 削峰处理 :
- 请求进入 MQ 或线程池排队;
- 缓存提前拦截超卖请求。
- 恶意流量过滤 :
- 验证码、滑块、人机校验;
- 秒杀码或令牌机制控制请求资格。
三、性能优化要点
1. 系统性能关键因素
-
CPU 执行时间:
- 序列化、复杂逻辑、对象创建消耗大;
- 建议使用高效序列化协议(如 Protobuf、Hessian),减少嵌套与冗余字段。
-
线程切换开销:
- 控制线程数量,避免过多导致上下文切换影响效率。
-
等待时间(网络 IO、磁盘 IO):
- 异步非阻塞、增加线程数有一定提升,但收益有限。
2. 性能瓶颈判断与排查
- 若 CPU 占用率持续超过 95% ,通常是:
- 序列化/反序列化性能差;
- 方法执行时间长;
- 代码逻辑冗余。
- 若 QPS 已接近瓶颈但 CPU 未打满 ,说明:
- 有 IO 阻塞或锁竞争;
- 系统仍有优化空间。
可结合使用
jstack
进行 定时采样,观察方法是否频繁出现在调用栈中,从而判断是否存在锁等待或调用过于频繁。
四、库存扣减策略
1. 扣减时机
- 下单即减库存:下单后用户不付款,可能导致库存浪费;
- 付款后减库存:可能多个用户同时付款成功,导致超卖;
- 推荐做法 :预扣库存 + 超时未付款回滚释放。
2. 扣减方式
- 使用数据库
UPDATE ... WHERE
条件语句,判断库存是否足够;
五、系统兜底与保护机制
1. 降级策略
- 关闭非核心功能(如推荐、历史订单),保障下单主链路可用。
2. 限流手段
- 固定窗口、滑动窗口、漏桶、令牌桶;
- 控制请求进入核心系统的速率。
3. 熔断策略(拒绝服务)
- 当 CPU > 90%、Load 超出 2×核数时,立即拒绝新请求,避免系统拖垮。
总结
秒杀系统的优化,是 架构设计能力、性能调优能力与业务理解能力 的综合体现。
不是简单 "加机器""放缓存" 就能解决问题,而要从链路、流量、数据、依赖等多个维度系统性地优化,做到:
- 提前规划
- 实时控制
- 故障兜底