

春节抢红包、双十一秒杀、618大促……每当这些全民狂欢的时刻来临,用户手机中的优惠券便会如雪花般涌入。支撑这一场景的背后,是一套能够抵御巨大流量冲击的技术体系。本文将深入探讨如何从零开始,设计并搭建一个可承载10万级QPS的优惠券系统。
一、需求分析与核心模型
假设你是一家电商平台的技术负责人,面对运营同学提出的需求:“我们需要在活动期间为1亿用户发放优惠券,预估峰值时刻将有超过10万人同时抢券。” 要应对这个挑战,首先需要清晰定义系统的核心模型。
一个优惠券系统的核心主要围绕两个实体展开:

- 券模板:如同“印钞机的模具”,它定义了优惠券的所有静态属性,例如面额、使用门槛、总发行数量、有效期等。
- 券记录:即实际发放到用户手中的“钞票”,它记录了动态信息,包括领取用户、领取时间、使用状态及核销时间等。
二、技术选型:构建稳固的基石
明确需求后,选择合适的技术组件是确保系统高性能与高可用的前提。
- 持久化存储:MySQL。券模板和券记录需要持久化存储,并支持丰富的条件查询(如“查询某用户所有未使用的优惠券”),关系型数据库 MySQL 以其稳定性和强大的查询能力成为首选。
- 高性能缓存:Redis。在10万QPS的并发压力下,直接查询数据库将瞬间导致瓶颈。Redis作为内存数据库,读写速度远超MySQL,非常适合存储热点数据,如券模板信息、实时库存等。
- 异步与定时:RocketMQ。处理优惠券过期等定时任务,若采用数据库轮询扫描的方式效率极低。RocketMQ提供的延迟消息功能,能够像“定时闹钟”一样,在指定时间触发业务逻辑,优雅地解决此类问题。
整体技术栈一览:

三、系统架构全景与数据模型
基于以上选型,我们可以勾勒出系统的整体架构与数据模型。
系统架构图展示了核心组件及其交互关系:

数据库设计直观地体现了核心实体关系:

四、核心业务流程拆解
4.1 发券流程:保障安全与一致性
发券是系统最核心且并发最高的操作。我们设计了包含参数校验、幂等校验、库存扣减三道关卡的流程来确保其安全与准确。

为何需要幂等校验? 在网络交互中,请求可能因超时而被客户端或网关重试。假设用户点击“领券”后网络延迟,请求被自动重发3次,若无幂等控制,用户可能意外领取3张券,给平台造成资损。通过基于唯一流水号的幂等校验,可以确保同一请求无论执行多少次,结果都一致。
4.2 券过期处理:循环延迟消息机制
RocketMQ的延迟消息存在时长上限(例如最多延迟2小时),但优惠券的有效期可能长达30天。为此,我们设计了“循环闹钟”机制:

具体流程:为一张30天后过期的券创建一条2小时的延迟消息。2小时后消费者收到消息,检查发现券未到期,则重新创建一条新的2小时延迟消息。如此循环,直至券真正到达过期时间点,才执行状态更新为“已过期”的操作。
五、高并发挑战与架构演进
基础功能实现后,面临10万QPS的真实压力,系统将遭遇一系列严峻挑战。
5.1 存储瓶颈:分库分表,分散压力
问题诊断:
- 单机MySQL的写入能力瓶颈约在4000 QPS。
- 单Redis分片的写入瓶颈约在2万 QPS。
- MySQL单表数据量过亿后,查询性能会显著下降。
解决方案:分而治之。
核心思想是通过分库分表将集中的流量和存储分散到多个节点上。
- 读写分离:将大量查询流量(如查券、查记录)导向MySQL只读从库,减轻主库压力。
- 水平拆分:
- 对于券记录表,以
user_id 的后四位作为分片键进行分表。这样,查询特定用户的所有优惠券时,可直接路由到特定分片,高效精准。
- 将不同的表分片部署到不同的MySQL服务器实例上,实现写能力的水平扩展。
- 同样,对Redis进行分片部署,分散缓存读写压力。

容量预估:为支撑12万QPS的发券请求,理论上需要 MySQL 分片数 ≈ 120000 / 4000 = 30个;Redis 分片数 ≈ 120000 / 20000 = 6个。
5.2 热点库存问题:库存Key拆分
问题诊断:当所有用户争抢同一张热门优惠券(如“新年红包券”)时,该券的库存Key会落在某个固定的Redis分片上,形成热点,导致该分片成为性能瓶颈。

解决方案:将一个全局库存Key拆分成多个子库存Key(例如 stock:coupon_1001_01, stock:coupon_1001_02, ...),并散列在不同分片上。


扣减库存时,随机或轮询选择一个子库存进行扣减。这样,并发请求被均匀分摊到多个Redis分片。还可进一步优化:当某个子库存扣减至零后,将其标记并跳过,避免无效查询。
5.3 缓存击穿与超时:本地二级缓存兜底
问题诊断:发券时必须查询券模板信息,严重依赖Redis。在实际压测中,Redis可能出现万分之2~3的超时。在10万QPS下,这意味着每秒有20-30个请求可能因此失败。
解决方案:引入应用层本地缓存(如Caffeine、Guava Cache)作为二级缓存。

当Redis查询失败或超时时,降级查询本地内存中的副本。本地缓存访问速度在纳秒级,几乎不存在超时风险。通过后台定时任务异步更新本地缓存,保证数据的最终时效性。
六、系统稳定性保障体系
功能与性能达标后,必须建立完善的稳定性保障体系,以应对生产环境中的各种不确定性。

- 超时与熔断:为发券接口设置合理的超时时间(如500ms,远大于正常处理的100ms)。当请求超时或下游连续失败时,快速熔断,防止个别慢请求耗尽系统资源。
- 全链路监控:搭建可视化监控仪表盘(如Grafana),实时追踪QPS、成功率、响应时间(P99/P95)等核心指标。设置智能报警,确保问题第一时间被发现。
- 限流防护:针对不同的上游调用方配置合理的限流规则,防止因某个业务方流量激增而拖垮整个优惠券服务。
- 资源与故障隔离:服务部署在多个可用区的Docker或Kubernetes集群中,实现故障域隔离,确保单机房故障不影响全局服务。
七、总结与核心思路
从零构建一个十万QPS级别的优惠券系统,是一个循序渐进、分层设计的过程:
- 功能实现:首先完成券模板管理、发券、过期处理等核心业务闭环。
- 技术选型:根据业务特性,选择合适的存储(MySQL)、缓存(Redis)、消息队列(RocketMQ)等组件。
- 性能优化:通过分库分表分散存储压力,通过库存拆分解决热点问题,通过二级缓存提升读取韧性,以应对高并发场景。
- 稳定护航:最后,通过超时熔断、监控报警、限流隔离等措施,构建系统的稳定性防线。
其核心设计思想始终是分而治之:流量分散、数据分散、风险分散。结合精确的容量预估与周全的稳定性设计,方能在流量洪峰面前屹立不倒。希望本次对高并发优惠券系统架构的剖析,能为你的系统设计带来启发。