共计 1881 个字符,预计需要花费 5 分钟才能阅读完成。
秒杀场景的典型痛点
在高并发秒杀场景中,我们最常遇到两个致命问题:
- 库存超卖 :当 100 个库存被 1 万个请求同时抢夺时,传统数据库的
UPDATE stock SET count=count-1会在并发下出现负库存 - 接口响应慢:瞬时流量导致数据库连接池耗尽,页面卡死在 ” 正在抢购 ” 状态
去年双十一我们系统就因此损失了 300+ 订单,事后用 jmeter 回放请求时发现:
- 5000QPS 直接打满 MySQL 连接
- 超卖比例高达 15%
技术方案横评
方案 1:同步锁(不推荐)
// 伪代码示例
public synchronized void deductStock() {if(stock > 0) {stock--;}
}
– 优点:实现简单
– 缺点:
– 单机锁无法跨服务
– 吞吐量 <100QPS
方案 2:异步队列(推荐)

1. 请求先进入 Kafka/RocketMQ
2. 消费者集群异步处理
- 优点:支持 10W+QPS
- 缺点:存在秒杀结果延迟
方案 3:分布式事务(慎用)
- 适用场景:需要强一致性的支付系统
- 秒杀场景 overkill
核心实现细节
Redis+Lua 原子扣减
-- KEYS[1]: 库存 key
-- ARGV[1]: 扣减数量
local stock = tonumber(redis.call('GET', KEYS[1]))
if stock >= tonumber(ARGV[1]) then
return redis.call('DECRBY', KEYS[1], ARGV[1])
end
return -1
关键点:
- 使用
SCRIPT LOAD预加载 Lua 脚本 - 通过
EVALSHA执行减少网络传输
RabbitMQ 削峰配置
spring:
rabbitmq:
listener:
simple:
prefetch: 50 # 每个消费者最大未 ack 数
concurrency: 20 # 最小消费者数
max-concurrency: 100 # 动态扩容上限
多级缓存架构
graph TD
A[客户端] -->|Nginx 本地缓存 | B[边缘节点]
B -->|Redis 集群 | C[数据库]
- 第一层:Guava Cache 设置 3 秒过期
- 第二层:Redis Cluster 分片存储
完整代码示例(Java)
@RestController
public class SeckillController {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// Lua 脚本 SHA1
private String stockScriptSha;
@PostConstruct
public void init() {
String script = "..."; // 前述 Lua 脚本
stockScriptSha = redisTemplate
.execute(connection -> connection.scriptLoad(script.getBytes()));
}
@PostMapping("/seckill")
public Result seckill(@RequestParam Long itemId) {
try {
Long result = redisTemplate.execute((RedisCallback<Long>) connection ->
connection.evalSha(stockScriptSha,
ReturnType.INTEGER,
1,
("stock_" + itemId).getBytes(),
"1".getBytes())
);
if (result == -1L) {return Result.fail("库存不足");
}
// 发送 MQ 消息
sendToMQ(itemId);
return Result.success();} catch (Exception e) {log.error("秒杀异常", e);
// 重试 3 次
return retry(() -> seckill(itemId), 3);
}
}
}
生产环境要点
缓存异常预防
- 穿透:
- 空值缓存:
redis.set("empty", "", 5min) -
BloomFilter 前置校验
-
雪崩:
- 随机过期时间:
TTL = baseTime + random(0, 300) - 永不过期的后台更新策略
监控指标
| 指标名称 | 阈值 | 告警方式 |
|---|---|---|
| Redis QPS | >50000 | 企业微信 |
| MQ 堆积量 | >10000 | 短信 |
| 接口 99 线 | >500ms | 邮件 |
开放性问题
在最终一致性方案中,我们可能会遇到:
- 用户支付成功后查不到订单(同步延迟)
- 超卖后的补偿退款流程
这需要根据业务容忍度权衡:
- 金融级系统:采用 TCC/SAGA
- 电商秒杀:消息队列 + 定时对账
实际项目中,我们通过夜间跑批修复了 0.03% 的数据不一致,这个代价远低于强一致性带来的性能损耗。
正文完
