优惠券核销高并发场景下,限流的核心答案就一句话:在用户维度、券批次维度和接口网关三层同时设置合理的流量阈值,用Redis做计数器,把超出阈值的请求挡在业务逻辑之前。
优惠券核销并发量太大怎么办?先想清楚限流粒度
用户级、批次级、接口级,到底卡哪一层
先看具体场景:某外卖平台的餐饮券,周三上午10点开抢,10:00:00那一刻,几十万人同时点“立即使用”,如果只在接口网关做统一限流,可能把普通用户也拦了,行业共识认为,限流的第一原则是“精准拦截”,宁可早点挡掉黄牛脚本,也别误伤真实用户。
来看限流的三个维度怎么定:
- 用户维度限流:单个用户每秒核销请求不超过1次,Redis里存userId,过期时间设1秒,用INCR加EXPIRE实现,逻辑简单,压测效果基本稳定。
- 券批次维度限流:同一批券的核销总并发控制在预设值,比如某批券限量2万张,系统每秒最多处理5000个核销请求,其余全部排队,这个维度防的是瞬间洪峰打爆数据库。
- 接口维度限流:对整个核销接口做QPS上限,作为兜底防线,不建议用固定阈值,按历史峰值的5倍左右设置更稳妥。
实际落地路径建议:
- 先上线Redis计数器,观察真实QPS曲线;
- 再根据曲线确定三档阈值:正常水位、预警水位、拒绝水位;
- 最后在API网关配置规则,比如简米云API网关或自建Sentinel。
优惠券核销限流与熔断有什么区别
很多团队把限流和熔断混为一谈,这正是大促翻车的常见原因。
限流是主动挡,熔断是被动断,限流在请求进入系统之前就把超标的流量拦下;熔断是下游系统已经开始报错、超时率达到阈值时,自动切断部分流量给下游喘息机会,两者在优惠券核销场景里的分工很明确:
- 核销请求流量过大 → 限流处理;
- 下游库存或支付系统响应变慢 → 熔断兜底。
| 维度 | 限流 | 熔断 |
|---|---|---|
| 触发时机 | 流量超过预设阈值 | 错误率或延时超过阈值 |
| 核心目标 | 拦截超额请求 | 保护下游系统 |
| 典型实现 | Redis计数器、令牌桶 | Sentinel熔断规则、Hystrix |
| 恢复方式 | 时间窗口滑动自动恢复 | 熔断器半开状态试探恢复 |
两者还需要配合前端文案区分:限流拦截后提示“请求太快请稍后重试”,熔断触发后提示“系统繁忙请稍后再来”,否则用户看不懂,会一直点重试,把系统推向更糟的状态。
高并发优惠券核销Redis限流方案,三个可落地的做法
固定窗口:最朴素,但够用
固定窗口的逻辑是把时间切成固定大小的时间段,无论1秒还是1分钟,窗口内最多放行N个请求,Redis里三条命令就能完成:
INCR(key)自增计数器;EXPIRE(key, 1)设置过期时间;- 判断当前计数是否超过阈值。
这个方案的问题在于窗口临界点,前1秒最后100ms过来1000个请求,后1秒前100ms又过来1000个请求,实际是100ms内打过来2000个,超出了预期每秒1000的容量,优惠券核销场景里,临界点恰恰是最高危的几秒,所以固定窗口适合用户维度的粗粒度限流,不适合接口维度的精确限流。
滑动窗口:解决临界爆发问题
把固定窗口改成滑动窗口,统计过去1秒内的真实请求数,Redis里用ZSET实现:
- 每次请求用
ZADD加入当前时间戳; - 用
ZREMRANGEBYSCORE清掉1秒之前的数据; - 用
ZCARD统计窗口内请求总数。
这三步必须包装成Lua脚本原子执行,避免并发下统计失真,滑动窗口在高流量下有额外性能消耗,但核销接口的流量往往秒级爆发,精度比损耗更值钱。在券批次维度做精确限流时,滑动窗口比固定窗口可靠得多。
令牌桶:适合把瞬时洪峰摊平
令牌桶和窗口方案的核心差异在于:窗口方案只管放不放,令牌桶还控制放行速率,比如某批次券允许核销峰值每秒2000,但突发流量到每秒8000,令牌桶会让超额请求排成队,以每秒2000的速率慢慢消化,而不是一刀切拒绝。
Go语言的golang.org/x/time/rate、Java的Guava RateLimiter都内置了令牌桶,选型建议是:网关层用云厂商的API网关自带限流插件,业务层用自己实现的令牌桶。
这里有个真实存在但很容易被无视的坑:常见的云Redis实例有连接数上限,而限流组件本身就是高频请求。如果没有提前估算好连接池大小,限流组件会比业务系统先被打挂。
同规格的单机Redis,云托管和自建运维的价格差异不算特别大,但云托管免运维,小团队更划算,地处华东的某外卖平台技术团队,大促前专门给限流Redis升配了连接数,就是把这一步计算到了预案里。
大促优惠券核销接口性能优化,远不止限流
限流只是第一道门,核销链路还包括券状态校验、库存扣减、异步通知、财务对账,任意一环卡住都会引发连锁问题。
异步化削峰:先收后放
核销请求进来之后不进数据库,先落到消息队列,MQ消费端按固定速率处理,队列积压越多说明当前压力越大,消费端可以动态扩容,这样即便接口限流拦住了一部分流量,放行的那部分也能平稳落到业务系统。
多级缓存:兜住查询流量
用户点开券包会先查券状态,这个查询量通常是核销量的十几倍,用本地缓存(Caffeine或ConcurrentHashMap)做第一层,Redis做第二层,数据库只承担最终一致性校验,记住一条原则:限流只挡写请求,读请求要用缓存消化。
降级预案:大促前必须演练
每年大促前,技术团队至少做两次全链路压测,其中一次专门验证限流规则是否生效,降级方案分三个等级:
- 一级降级:关闭非核心通知,保住核销主流程;
- 二级降级:核销结果异步化,用户先看到“已受理”;
- 三级降级:启用排队页面,流量在系统入口层就被挡住。
三级降级的核心数据是:单机核销接口处理能力能到多少TPS,网关限流阈值必须低于这个数字的80%,否则Redis放行的流量仍然会打满数据库连接池,据统计,历年大促核销接口的流量曲线都是瞬时尖峰,压测模型不要用匀速流量,一定要模拟这种尖峰形态。
优惠券核销接口限流常见问题
Q1:优惠券核销限流阈值怎么确定?
先压测,再看监控,最后定阈值,具体做法:用JMeter或wrk对核销接口做阶梯加压,找到性能拐点,就是CPU、内存、数据库连接池全部恶化的那个QPS数值,然后把限流阈值设为拐点值的60%到70%,预留的30%左右给瞬时波动和容器扩容留白,上线之后根据大促当天实际表现动态调整,不能一次定死。
Q2:限流和熔断在优惠券核销场景里怎么配合?
限流挡在最前面,熔断守在下游,限流保证进入系统的流量不超过处理能力,熔断保证依赖系统故障时请求能快速失败而不是白白占住线程池,具体配合方式是在核销接口的依赖调用处配置熔断规则,错误率超过阈值直接短路,同时网关层限流阈值动态调低,形成双层防护,这套配合在最近几年电商大促的公开复盘里多次被印证。
Q3:限流导致用户核销失败率高怎么办?
限流拦截的请求本来就不该进入系统,所以真正要看的失败率是系统内部的真实失败率,而不是用户侧的重试次数,降低用户侧失败感有两种方法:一是返回可控的重试时间,提示“请稍后2分钟再试”;二是把超额请求转成异步排队,用户看到“核销已受理”就先离开,最终结果通过推送通知告知,第二种方法对大促体验改善非常明显,但前提是把异步队列的积压处理能力提前压测到位。
限流不是万能药,但优惠券核销高峰没有限流,系统崩起来连追溯的机会都没有,把限流粒度、算法选型和降级预案配套落地,核销链路才真正扛得住大促流量。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/772468.html


