卡飞资源网

专业编程技术资源共享平台

【秒杀】支撑单场 50 万+人次的秒杀方案

一、秒杀系统核心挑战

在巨星演唱会门票等热门商品的秒杀场景中,系统将会面临两大核心挑战:

  1. 库存准确性要求:必须确保门票不发生超卖现象
  2. 系统稳定性要求:必须保障用户体验,避免高并发流量导致系统发生崩溃

二、整体架构设计思路

我们的秒杀系统采用分层防御策略,将流量控制前移,避免所有压力集中在后端服务:

用户层 → 前端限流 → 网关层 → 服务层 → 数据层

三、前端流量控制方案

  1. 预约机制

1.热门巨星演唱会门票需提前预约,未预约用户无法参与秒杀 2.实现原理:预约数据存储在 Redis,秒杀时快速校验

  1. 人机验证

1.高峰期启用验证码(常规图形验证码) 2.明星周边等超热门商品采用问答式验证(如与明星相关的问题) 3.实现技术:Google reCAPTCHA或自研验证服务

  1. 随机请求丢弃

1.前端按策略随机返回"系统繁忙"或"已售罄" 2.丢弃比例根据实时系统负载动态调整 3.实现方式:客户端JavaScript逻辑+服务端下发的控制参数

  1. 精准限流算法
// 示例:基于系统能力的动态限流
int maxQPS = 3000; // 系统最大承受QPS
int stock = 500;   // 本次秒杀库存量
int allowedQPS = (int)(maxQPS * 0.8); // 保留20%缓冲
if(stock < 1000) {
    allowedQPS = Math.min(allowedQPS, stock * 5); // 库存少时更严格
}

四、防重复下单机制

Token校验流程

  1. 生成Token:页面加载时服务端生成唯一Token(如UUID)
  2. 携带Token:用户提交订单时附带该Token
  3. 验证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;
  1. Token 失效:无论成功与否,Token一次性使用后立即失效

五、库存扣减设计方案

三级库存保护机制

  1. Redis 预扣减:第一层快速过滤
  2. 数据库确认扣减:第二层最终一致性保证
  3. 支付最终扣减:第三层实际减少

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 库存回滚 + 数据库状态更新

八、总结与最佳实践

  1. 分层防御:流量控制要前置,避免所有压力到数据库
  2. 多级校验:前端、网关、服务多层校验
  3. 最终一致:接受短暂的不一致,确保最终正确
  4. 降级方案:准备好熔断策略(如直接返回售罄)

通过这套架构,我们成功支撑过单场 50 万+人次的秒杀活动,系统保持零超卖、零崩溃的记录。

实际生产中建议根据具体业务需求调整各层参数,并通过全链路压测验证系统极限。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言