六个机器学习算法简洁实现与调参避坑指南
2026/9/26 4:22:14
在高并发业务场景中,秒杀系统几乎是所有后端工程师绕不开的一道“必修课”。 它同时考验:
一个合格的秒杀系统,至少要解决四个核心问题:
本文将基于一个完整的:
Spring Boot + Redis + Lua + 消息队列 的秒杀系统方案
从 0 到 1 设计一个生产级高并发秒杀系统,并且:
基础架构如下
这基本就是淘宝、京东、美团等互联网公司秒杀的真实架构缩影。
在开始写代码之前,必须先统一系统设计目标:
库存扣减必须是不可分割的操作:
判断库存 → 扣减库存 → 记录用户 → 生成订单信息 必须要么全部成功,要么全部失败这正是 Lua 脚本存在的意义。
同一个用户:
你现在已经在 Redis 层用:
SISMEMBER userSetKey userId做了第一层幂等,这是非常专业的设计。
后面我们会补上:
形成 双重保险。
秒杀流量是典型的:
1 秒内爆发 → 10 万 QPS但数据库最多只能抗几百 QPS。
所以必须:
请求 → Redis 快速失败 成功请求 → MQ 排队 MQ → 平滑写 DBRedis + MQ 是整个系统“抗洪水”的核心。
Redis 扣库存成功 ≠ 订单一定成功。
可能发生:
因此我们必须引入:
后面会专门一节讲。
任何系统都会出问题:
| 故障 | 应对 |
|---|---|
| Redis 慢 | 返回“排队中” |
| Redis 挂 | 秒杀入口关闭 |
| MQ 挂 | 只做 Redis 预扣,不下单 |
| DB 慢 | 订单排队 |
秒杀系统不是“永远成功”,而是永远可控失败。
| 组件 | 作用 |
|---|---|
| Spring Boot | 服务骨架 |
| Redis | 高性能缓存 + 原子操作 |
| Lua | 原子库存扣减 |
| Redisson | 分布式锁 / 限流 |
| MQ(RabbitMQ/Kafka) | 削峰 + 异步 |
| MySQL | 最终订单存储 |
| Prometheus + Grafana | 监控告警 |
在秒杀系统中,库存扣减是整个系统最敏感、最关键的一环。 如果这里出问题,后面所有架构设计全部失去意义。
为什么必须用 Lua?
因为:
即:
Lua = 分布式系统里的“原子事务”
在你的方案中,Key 设计非常规范:
| Key | 含义 |
|---|---|
| seckill:stock:{productId} | 商品库存 |
| seckill:users:{productId} | 已成功秒杀用户集合 |
| seckill:order:{orderId} | 订单信息 |
| seckill:activity:{productId} | 活动信息 |
| seckill:sold:{productId} | 已售数量(统计用) |
这是一个非常“工程化”的 Key 设计,既方便业务,又方便运维。
你设计的 Lua 脚本已经是生产级写法,这里完整保留:
-- KEYS[1]: 商品库存key -- KEYS[2]: 秒杀成功用户集合key -- ARGV[1]: 用户ID -- ARGV[2]: 购买数量 -- ARGV[3]: 活动开始时间 -- ARGV[4]: 活动结束时间 -- 1. 参数校验 if #KEYS ~= 2 or #ARGV ~= 4 then return -1 -- 参数错误 end local stockKey = KEYS[1] local userSetKey = KEYS[2] local userId = ARGV[1] local quantity = tonumber(ARGV[2]) local startTime = tonumber(ARGV[3]) local endTime = tonumber(ARGV[4]) -- 2. 检查活动时间 local currentTime = redis.call('TIME')[1] if currentTime < startTime then return -2 -- 活动未开始 end if currentTime > endTime then return -3 -- 活动已结束 end -- 3. 检查用户是否重复购买 local exists = redis.call('SISMEMBER', userSetKey, userId) if exists == 1 then return -4 -- 重复购买 end -- 4. 获取库存并检查 local stock = redis.call('GET', stockKey) if not stock then return -5 -- 商品不存在 end stock = tonumber(stock) if stock <= 0 then return -6 -- 库存不足 end if stock < quantity then return -7 -- 库存不足 end -- 5. 扣减库存 redis.call('DECRBY', stockKey, quantity) -- 6. 添加用户到已购买集合 redis.call('SADD', userSetKey, userId) -- 7. 设置用户过期时间(活动结束后1小时) redis.call('EXPIREAT', userSetKey, endTime + 3600) -- 8. 记录订单信息到Hash local orderId = string.format("ORDER:%s:%s", userId, currentTime) local orderKey