一、传统秒杀痛点
原始流程全部压力落在 MySQL:查询库存、判断用户是否下单、扣库存、创建订单,大量并发请求直接打数据库,MySQL 扛不住高并发。
二、Redis 优化整体架构
- Nginx:接收前端大量请求,做负载均衡。
- Redis 层(Lua 脚本) :库存校验、一人一单校验、扣减库存、记录已下单用户,全部在 Redis 内部原子完成,拦截绝大部分无效请求。
- 阻塞队列 :Redis 校验成功后,把
优惠券ID、用户ID丢入阻塞队列,直接给前端返回订单 ID。 - 异步消费者:后台线程从队列取出消息,异步操作 MySQL 完成真正的创建订单、扣数据库库存。
核心思想:Redis 做前置校验过滤流量,数据库异步慢处理,把高并发挡在 Redis,保护 MySQL。
三、Lua 脚本作用
Redis 单线程执行 Lua 脚本,整个脚本具备原子性,中间不会被其他命令打断,避免超卖、重复下单并发问题。
Lua 脚本业务逻辑
输入参数:voucherId优惠券ID、userId用户ID
- 判断库存:读取
seckill:stock:{voucherId}库存值- 库存≤0 → 返回
1,代表库存不足
- 库存≤0 → 返回
- 判断用户是否已经抢购过:使用 Set 集合
seckill:order:{voucherId}保存已下单用户 IDsismember判断用户已存在 → 返回2,代表重复下单
- 校验全部通过:
incrby对库存 key 做 - 1 扣减库存sadd把 userId 写入 Set 集合标记该用户已下单- 返回
0,代表校验成功,可以下单
返回码含义
表格
| 返回值 | 含义 |
|---|---|
| 0 | 校验通过,允许下单,进入异步队列 |
| 1 | 库存不足 |
| 2 | 用户重复下单(一人一单限制) |
四、完整业务执行流程
- 用户发起秒杀请求,进入服务。
- Java 调用 Redis 执行 Lua 脚本。
- 判断脚本返回结果:
- 返回
1/2:直接向前端返回失败提示。 - 返回
0:把业务数据放进阻塞队列,立刻返回订单 ID 给前端。
- 返回
- 后台独立线程不断消费阻塞队列:
- 查询数据库
- 扣减 MySQL 真实库存
- 创建订单记录