一、秒杀系统核心挑战
在巨星演唱会门票等热门商品的秒杀场景中,系统将会面临两大核心挑战:
- 库存准确性要求:必须确保门票不发生超卖现象
- 系统稳定性要求:必须保障用户体验,避免高并发流量导致系统发生崩溃
二、整体架构设计思路
我们的秒杀系统采用分层防御策略,将流量控制前移,避免所有压力集中在后端服务:
用户层 → 前端限流 → 网关层 → 服务层 → 数据层
三、前端流量控制方案
- 预约机制
1.热门巨星演唱会门票需提前预约,未预约用户无法参与秒杀 2.实现原理:预约数据存储在 Redis,秒杀时快速校验
- 人机验证
1.高峰期启用验证码(常规图形验证码) 2.明星周边等超热门商品采用问答式验证(如与明星相关的问题) 3.实现技术:Google reCAPTCHA或自研验证服务
- 随机请求丢弃
1.前端按策略随机返回"系统繁忙"或"已售罄" 2.丢弃比例根据实时系统负载动态调整 3.实现方式:客户端JavaScript逻辑+服务端下发的控制参数
- 精准限流算法
// 示例:基于系统能力的动态限流
int maxQPS = 3000; // 系统最大承受QPS
int stock = 500; // 本次秒杀库存量
int allowedQPS = (int)(maxQPS * 0.8); // 保留20%缓冲
if(stock < 1000) {
allowedQPS = Math.min(allowedQPS, stock * 5); // 库存少时更严格
}
四、防重复下单机制
Token校验流程
- 生成Token:页面加载时服务端生成唯一Token(如UUID)
- 携带Token:用户提交订单时附带该Token
- 验证Token:
// Redis原子性校验
String script =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
Long result = redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList("order:token:" + userId),
token
);
return result == 1;
- Token 失效:无论成功与否,Token一次性使用后立即失效
五、库存扣减设计方案
三级库存保护机制
- Redis 预扣减:第一层快速过滤
- 数据库确认扣减:第二层最终一致性保证
- 支付最终扣减:第三层实际减少
Redis 预扣减 Lua 脚本
-- KEYS[1]: 库存key
-- ARGV[1]: 扣减数量
-- ARGV[2]: 活动开始时间
-- ARGV[3]: 活动结束时间
local stock = tonumber(redis.call('GET', KEYS[1]))
local now = tonumber(ARGV[4]))
-- 校验活动时间
if now < tonumber(ARGV[2]) then
return -1 -- 活动未开始
end
if now > tonumber(ARGV[3]) then
return -2 -- 活动已结束
end
-- 校验库存
if not stock or stock < tonumber(ARGV[1]) then
return 0 -- 库存不足
end
-- 执行扣减
redis.call('DECRBY', KEYS[1], ARGV[1])
return 1 -- 成功
六、数据库层解决方案
方案一:InventoryHint 技术(阿里云方案)
/*+ COMMON_EXPRESSION( `inventory` >= 1 ) */
UPDATE products
SET inventory = inventory - 1
WHERE id = 10086 AND inventory >= 1
优势:
- 数据库引擎层直接实现行排队更新
- 无需额外中间件
- 性能接近原生 SQL
方案二:MQ 异步处理
Redis + MQ 处理流程
- Redis预扣减成功
- 发送MQ消息(包含用户ID、商品ID、数量)
- 消费者顺序处理:
@RabbitListener(queues = "stock.queue")
public void process(StockMessage message) {
// 数据库行锁保证原子性
productMapper.reduceStock(
message.getProductId(),
message.getQuantity()
);
// 创建订单
orderService.createOrder(...);
}
七、异常处理与补偿
1.Redis 与数据库不一致:
- 定时任务核对库存
- 自动补偿机制:
// 每日执行的库存核对
List<Discrepancy> discrepancies = stockService.checkDiscrepancy();
discrepancies.forEach(d -> {
if(d.getRedis() > d.getDb()) {
redisTemplate.opsForValue().set(
"product:stock:"+d.getProductId(),
d.getDb()
);
}
});
2.支付超时处理:
- 15 分钟未支付自动释放预扣库存
- Redis 库存回滚 + 数据库状态更新
八、总结与最佳实践
- 分层防御:流量控制要前置,避免所有压力到数据库
- 多级校验:前端、网关、服务多层校验
- 最终一致:接受短暂的不一致,确保最终正确
- 降级方案:准备好熔断策略(如直接返回售罄)
通过这套架构,我们成功支撑过单场 50 万+人次的秒杀活动,系统保持零超卖、零崩溃的记录。
实际生产中建议根据具体业务需求调整各层参数,并通过全链路压测验证系统极限。