下单秒杀项目随笔
一、前言
最近部门里的秒杀项目出了点问题,服务不可用+超卖 (主要还是低估了请求量),所以最近研究学习下秒杀相关的项目
二、主要解决问题
-
高并发访问问题
秒杀开始时,大量用户同时请求(如3000QPS以上),远超日常流量,可能导致服务器崩溃、数据库瘫痪 (主要是数据库无法支持大量并发请求,单机16G内存的mysql估计qps在1~2千,如果是写操作估计更少)
解决方法:通过分层削峰(如前端限流、消息队列异步化)、横向扩展(集群化)、缓存优化(Redis预加载库存)降低直接压力(最常用的方法还是redis缓存+MQ)。 -
资源竞争与超卖
库存有限(如100件商品),并发请求可能导致超卖(库存减为负数)或数据不一致。
解决方法:
原子操作:通过Redis的DECR或Lua脚本保证库存扣减的原子性。
数据库乐观锁:通过版本号或CAS(Compare-And-Swap)机制更新库存。 -
系统稳定性与熔断
问题:秒杀可能拖垮整个系统,影响其他正常服务。
解决方法:
服务隔离:独立部署秒杀模块,避免资源争抢。
降级策略:超时后返回静态页面或排队结果,保护后端。 -
公平性与防作弊
暂不考虑
三、方案架构与思路

整体思路:在redis这里处理库存扣减,将99%的无效请求拦截,防止大量请求打入mysql 。
扣减库存成功后创建订单消息放入MQ异步处理,防止用户等待时间太长,服务器处理时间太久。
核心逻辑:
3.1 Golang 服务设计
点击查看代码
// AI生成的示例代码:核心秒杀逻辑 (Gin框架)
func SeckillHandler(c *gin.Context) {
// 1. 用户鉴权 & 防刷校验
userID := auth(c)
if isBlocked(userID) { // Redis记录用户请求频率
c.JSON(403, "请求过于频繁")
return
}
// 2. Redis预检库存 (减少无效请求穿透)
stockKey := "seckill:stock:" + itemID
stock, err := redis.Decr(stockKey) // 原子扣减
if stock < 0 {
redis.Incr(stockKey) // 回滚
c.JSON(400, "已售罄")
return
}
// 3. 生成唯一订单号,投递消息队列
orderID := generateOrderID()
msg := json.Marshal(Order{UserID: userID, ItemID: itemID, OrderID: orderID})
kafka.Publish("seckill_orders", msg)
// 4. 返回排队中状态
c.JSON(200, gin.H{"status": "processing", "order_id": orderID})
}
3.2 Redis 关键操作
库存预热:秒杀前将库存加载到Redis(SET seckill:stock:1001 100)。
原子扣减:使用DECR或Lua脚本保证原子性
3.3 MySQL 数据最终一致
订单表:orders(id, user_id, item_id, status, created_at),status标记为“未支付/已完成”。
库存表:items(id, stock, version),通过乐观锁(version字段)防止超卖:

浙公网安备 33010602011771号