秒杀排队不能只用ratelimiter,因其为单实例内存限流,k8s多pod部署时qps=单桶限速×实例数,无法抵御洪峰;健康检查路径与秒杀路径共享桶导致probe失败重启;burst非并发上限而是可借令牌数,redis响应慢时令牌不归还致吞吐断崖下跌;必须依赖redis zset实现跨实例一致排队,并反馈“前面还有x人”。

秒杀排队为什么不能只用 rate.Limiter
因为 rate.Limiter 是单实例内存态限流,K8s 多 Pod 部署时各跑一个桶,实际放行 QPS = 单桶限速 × 实例数,根本挡不住洪峰;健康检查路径(如 /health)和秒杀路径共享同一个桶,probe 失败直接触发 Pod 反复重启;burst 不是并发数上限,而是“可借令牌数”,下游 Redis 响应慢时令牌长期不归还,吞吐量会断崖下跌。
排队机制必须跨实例一致,且要能反馈「前面还有 X 人」——这只能靠 Redis 的有序集合(ZSET)实现。
- 按请求维度建队列 key:
"seckill:queue:" + productID,避免不同商品互相阻塞 - score 用
time.Now().UnixMilli(),保证先进先出(FIFO)语义 - member 用唯一 traceID 或
userID + timestamp组合,防止同一用户重复入队 - 插入后立刻调
ZCARD返回当前排队人数,前端可实时展示
如何用 ZADD + EXPIREAT 实现带自动过期的排队
Redis 原生命令组合存在竞态:先 ZADD 再 EXPIREAT,中间若服务崩溃,key 就永久存在。正确做法是用 Lua 脚本原子执行:
local key = KEYS[1]
local score = ARGV[1]
local member = ARGV[2]
local ttl = tonumber(ARGV[3])
<p>redis.call('ZADD', key, score, member)
redis.call('EXPIREAT', key, ttl + tonumber(redis.call('TIME')[1]))
return redis.call('ZCARD', key)</p>
Go 调用时注意传入 ctx 并设超时(建议 ≤ 200ms),否则网络抖动会导致整个请求卡住:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
redis.Eval(ctx, script, []string{queueKey}, nowMilli, traceID, expireSec)- 返回值是当前排队人数,为 1 表示刚进队、且是队首,可立即走库存扣减逻辑
- 若返回值 > 1,前端轮询
ZRANGE queueKey 0 0 WITHSCORES查自己是否已到队首
怎么判断自己是不是队首并安全执行业务逻辑
不能只靠「队列长度 == 1」就执行,因为并发下多个请求可能同时看到长度为 1。必须用 ZRANGE key 0 0 WITHSCORES 拿出队首元素,比对 member 是否等于自己的 traceID。
- 如果匹配,说明你确实是下一个;此时立刻调
redis.Decr(stockKey)扣库存 -
redis.Decr返回值必须校验:≥ 0 才算成功,负数代表已售罄,需立刻redis.Incr回滚 - 扣减成功后,必须同步
ZREM queueKey traceID,否则队列越积越多 - 整个流程需包在
defer或recover中,panic 或网络中断时也要清理队列项
Redis 版本与 Lua 脚本执行安全边界
Redis 8.2.3(2026年6月发布)修复了 CVE-2025-62507 远程代码执行漏洞,该漏洞可被恶意构造的 Lua 脚本触发。如果你还在用 8.2.2 或更早版本,EVAL 脚本存在被绕过沙箱的风险。
生产环境务必升级到 8.2.3+,且禁止在脚本里调用 redis.call("KEYS", "*") 或遍历大集合——这类操作在高并发下极易打满 CPU。
真正容易被忽略的不是逻辑,而是时间精度:Lua 中 redis.call("TIME") 返回秒级时间戳,而 Go 侧用的是毫秒级 UnixMilli()。两者混用会导致排序错乱,必须统一用 redis.call("TIME")[1] * 1000 + redis.call("TIME")[2] / 1000 模拟毫秒精度,或干脆全用 Go 生成 score 后传入。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










