go高并发秒杀系统核心是channel限流容量、redis.decr返回值校验、异步落单边界切分;channel容量n须压测确定,需监控redis延迟、mysql threads_running及cpu/内存水位;redis.decr必须校验返回值,区分redis.nil场景。

Go 构建高并发秒杀系统,核心不是堆功能,而是用对三样东西:channel 限流容量、redis.Decr 的返回值校验、异步落单的边界切分。 其他都是围绕这三点展开的工程取舍。
channel 容量怎么设才不翻车
make(chan struct{}, N) 的 N 不是经验值,是压测出来的生死线。它直接决定 Redis 的 p99 延迟和 MySQL 的 Threads_running 水位。
- 监控
cmdstat_decr延迟,持续 > 5ms 就必须降N - MySQL 执行
SHOW STATUS LIKE 'Threads_running',若长期 > 200,说明 DB 已排队,N得缩容 - CPU 持续 > 70% 或内存逼近 90%,不是加机器,而是
N已超载——再撑下去会触发 OOM killer - 别用
sync.WaitGroup等异步任务完成,它不释放 channel 令牌,限流就失效了
redis.Decr 必须校验返回值,不能只看 err
redis.Decr 返回的是扣减后的值,不是布尔结果。不校验就写订单,等于主动送库存进负数。
- 返回
redis.Nil:要区分是活动未开始(key 不存在)还是配置漏了,不能直接 panic - 返回值 redis.Incr 回滚,否则网络中断或 panic 会导致库存永久缺失
- 禁用“先 GET 再 DECR”的 Lua 封装——破坏原子性;正确脚本只做
DECR+ 条件判断,且必须返回新值供 Go 层校验 - 别用
INCRBY做加库存兜底,秒杀场景下库存只减不增,DECR语义更清晰、出错路径更短
异步落单不是优化,是防止 503 的底线
秒杀成功那一刻,只要 redis.Decr 返回 ≥ 0、订单号生成完成,就必须立刻 HTTP 响应。后续所有操作全扔进异步队列。
- 同步写库一旦卡在 MySQL 行锁或主从延迟上,goroutine 就卡住不归还 channel 令牌,新请求直接 503
- 异步任务失败必须有重试 + 死信兜底,但重试不能影响用户侧响应时间——这是两层逻辑,不能混在一起
- 幂等下单必须用
SETNX,key 含场次 ID + 用户 ID,value 存毫秒时间戳或随机 token,过期时间 = 支付超时 + 30s - 本地
map或sync.Map在多实例部署下彻底失效;MySQL 唯一索引写入太慢,扛不住瞬时洪峰,会把 DB 打挂
真正难的不是写对一行 Decr,而是让 channel 容量、Redis 过期时间、SETNX 过期时间、异步队列重试策略这四组参数,在压测中达成脆弱平衡——差 10ms 超时、差 100 个 channel 容量、差 5s 过期时间,都可能让系统在峰值时刻静默崩塌。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











