从 10 万并发到稳定下单,如何构建高性能秒杀系统
秒杀系统是互联网最经典的高并发场景之一。面对瞬时数十万甚至百万级请求,传统同步下单模式会面临库存超卖、数据库雪崩、重复下单等问题。本文从一次秒杀请求的完整生命周期出发,介绍如何利用Redis、Lua脚本、消息队列构建一套高性能、高可用的秒杀架构,并结合生产实践分析幂等、最终一致性、限流等关键设计
一、为什么秒杀如此困难?
秒杀最大的特点不是业务复杂,而是流量极端集中。
平时一天的访问量,可能会在几秒钟内全部涌入系统。
如果仍然采用传统流程:
用户请求
↓
查询 MySQL 库存
↓
扣减库存
↓
创建订单
↓
提交事务那么数据库会成为整个系统最先崩溃的地方。
真正的挑战并不是"下单",而是如何让系统在极端并发下依然保持稳定。
二、秒杀系统真正需要解决的问题
成熟的秒杀系统通常需要同时解决以下几个问题:
- 库存不能超卖
- 一个用户不能重复下单
- 用户能够快速得到响应
- 数据库不能被高并发直接打爆
- 最终订单数据保持一致
因此,通常采用"缓存承接流量、消息异步处理、数据库最终落库"的整体设计。
三、整体架构设计
用户请求
│
▼
CDN / Nginx / Gateway
│
限流、鉴权、风控
│
▼
Redis + Lua 原子校验
│
┌────────────┴────────────┐
│ │
秒杀失败 秒杀成功
│ │
▼ ▼
直接返回 写入消息队列
│
▼
消费者异步创建订单
│
▼
MySQL整个架构遵循一个核心原则:
请求尽可能停留在缓存层,数据库只负责最终持久化。
四、为什么库存一定放 Redis?
很多开发者最初都会想到直接查询数据库库存。
但数据库擅长的是事务和持久化,而不是承载几十万 QPS 的热点访问。
Redis 基于内存存储,访问速度远高于磁盘数据库,同时能够轻松支撑高并发读写。
因此,通常会将秒杀库存提前预热到 Redis:
stock:10001 = 100
所有库存操作都首先发生在 Redis,数据库库存只作为最终结果保存。
五、Redis 为什么还需要 Lua?
很多人认为 Redis 是单线程,因此天然不会出现并发问题。
实际上,单条命令是原子的,多条命令不是。
例如:
- 获取库存
- 判断库存
- 扣减库存
这是三个独立命令。
两个请求完全可能在第一步读取到相同库存,从而导致库存异常。
Lua 的作用,就是把这些命令封装成一个不可分割的原子操作。
local stock = tonumber(redis.call("GET", KEYS[1]))
if stock <= 0 then
return -1
end
redis.call("DECR", KEYS[1])
return 1Redis 在执行 Lua 脚本期间不会插入其他命令,因此库存判断和扣减始终保持一致。
如果还需要校验重复下单,也可以一起放入 Lua,实现一次完成:
- 判断库存
- 判断用户是否已下单
- 扣减库存
- 写入用户购买标记
整个过程只执行一次网络请求。
六、为什么消息队列才是秒杀系统的核心?
很多文章介绍 MQ 时都会强调"异步"。
实际上,真正的价值是削峰。
假设活动开始后的 5 秒内收到 10 万个请求,而数据库每秒只能稳定处理 3000 个订单。
如果所有请求直接访问数据库:
100000 请求 │ ▼ MySQL │ ▼ 数据库崩溃
引入消息队列之后:
100000 请求 │ ▼ Redis + Lua │ ▼ Message Queue │ ▼ 3000 条 / 秒稳定消费 │ ▼ MySQL
消息队列把瞬时洪峰变成稳定流量,数据库始终运行在可承受范围内。
七、为什么用户秒杀成功,订单却还没有生成?
很多用户会发现:
页面提示"抢购成功",订单列表却没有立即出现。
这是因为:
秒杀成功并不代表订单已经写入数据库。
它表示:
- Redis 已完成库存扣减
- 秒杀请求已进入消息队列
真正的订单创建由后台消费者异步完成。
这样可以显著降低接口响应时间,让用户几十毫秒内获得反馈。
八、如何保证最终一致性?
缓存扣减成功,并不意味着数据库一定写入成功。
因此生产环境通常会增加以下机制:
消息重试
消费者执行失败后自动重新消费。
死信队列
超过最大重试次数后进入死信队列,等待人工处理。
幂等设计
消费者根据订单号或用户信息判断是否已经处理,避免重复创建订单。
补偿任务
定时扫描 Redis 与数据库数据,发现异常后自动修复。
互联网系统通常追求的是:
最终一致性,而不是绝对实时一致。
九、还会做哪些优化?
真正的生产环境远不止 Redis + Lua + MQ。
通常还会增加:
网关限流
通过令牌桶、漏桶算法限制异常流量。
动态 Token
秒杀接口增加一次性 Token,防止脚本直接调用。
热点 Key 优化
避免大量请求集中访问同一个 Key。
多级缓存
CDN、本地缓存、Redis 多级协同。
风控系统
限制恶意用户、设备、IP。
分库分表
订单达到亿级后进行水平拆分。
监控告警
实时监控库存、消息积压、消费失败率、接口耗时等核心指标。
这些优化共同构成了一套真正可落地的秒杀系统。
十、总结
秒杀系统的关键并不是"抢得快",而是在极端流量下依然稳定可靠。
整个架构中,各组件职责十分明确:
组件 核心职责
Redis 承接高并发库存访问 Lua 保证库存扣减与重复校验的原子性 消息队列 削峰填谷,实现异步解耦 消费者 异步创建订单、更新业务数据 MySQL 持久化最终业务数据
真正优秀的秒杀系统,本质上是一套职责清晰、分层合理的架构设计。
Redis 负责"快",Lua 负责"准",消息队列负责"稳",数据库负责"存"。
也正因为如此,这套架构不仅适用于电商秒杀,在抢票、预约、优惠券发放等各种高并发业务中同样得到了广泛应用。