下单秒杀项目随笔

一、前言

最近部门里的秒杀项目出了点问题,服务不可用+超卖 (主要还是低估了请求量),所以最近研究学习下秒杀相关的项目

二、主要解决问题

  1. 高并发访问问题
    秒杀开始时,大量用户同时请求(如3000QPS以上),远超日常流量,可能导致服务器崩溃、数据库瘫痪 (主要是数据库无法支持大量并发请求,单机16G内存的mysql估计qps在1~2千,如果是写操作估计更少)
    解决方法:通过分层削峰(如前端限流、消息队列异步化)、横向扩展(集群化)、缓存优化(Redis预加载库存)降低直接压力(最常用的方法还是redis缓存+MQ)。

  2. 资源竞争与超卖
    库存有限(如100件商品),并发请求可能导致超卖(库存减为负数)或数据不一致。
    解决方法:
    原子操作:通过Redis的DECR或Lua脚本保证库存扣减的原子性。
    数据库乐观锁:通过版本号或CAS(Compare-And-Swap)机制更新库存。

  3. 系统稳定性与熔断
    问题:秒杀可能拖垮整个系统,影响其他正常服务。
    解决方法:
    服务隔离:独立部署秒杀模块,避免资源争抢。
    降级策略:超时后返回静态页面或排队结果,保护后端。

  4. 公平性与防作弊
    暂不考虑

三、方案架构与思路

image

整体思路:在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字段)防止超卖:

posted @ 2026-07-15 18:44  chenqi1231  阅读(10)  评论(0)    收藏  举报