在电商大促活动中,秒杀场景无疑是技术架构面临的最大挑战之一。其核心难点在于:当瞬时流量远超日常数十甚至上百倍时,如何在保障极速响应的同时,确保整个系统的稳定运行?
本文将深入探讨一个能够支撑百万级并发请求的秒杀架构是如何从零到一搭建起来的,涵盖需求拆解、四层架构设计、关键细节避坑以及核心优化方向。
需求拆解:从模糊到可度量
面对“高并发”这样一个宽泛的需求,第一步是将其拆解为可落地、可衡量的具体技术指标。这需要从用户和技术的双重视角进行分析。
1. 用户视角:梳理核心业务流程
从用户点击“立即抢购”到收到成功通知,整个过程可以清晰地拆分为6个关键环节:

每个环节都至关重要且环环相扣。例如,用户资格校验未通过,则无需进行后续的库存检查。只有将完整流程细化拆解,才能明确在何处应设置拦截点,以及哪些环节可以进行异步化处理。
2. 技术视角:定义四大核心指标
明确业务流后,我们需要为系统设定可量化的健康指标。
2.1 并发能力指标
对于“双11”级别的活动,系统峰值 QPS(每秒查询率)需能支撑100万,而核心事务 TPS(每秒事务处理率)则需达到至少5万,以确保用户操作的流畅性。

2.2 稳定性与响应指标
99%的用户请求应在200毫秒内得到响应。对于秒杀这类核心业务,故障恢复时间目标(RTO)必须严格控制在5分钟以内。

2.3 数据一致性指标
库存数据必须保证100%准确,任何超卖都可能引发严重的客诉问题。同时,支付与订单状态的同步延迟不应超过10秒。
2.4 安全防护指标
系统需能有效拦截99%以上的自动化脚本攻击,防止黄牛在短时间内扫光库存。对于黑名单用户,必须实现100%的拦截。

以上四项指标为秒杀系统的技术设计确立了清晰的基准。
架构搭建:构建四层骨干体系
需求明确后,即可着手搭建系统骨架。一个健壮的秒杀架构通常包含接入层、流量削峰层、业务逻辑层和数据层,每层各司其职。
1. 接入层:构筑第一道防线
用户的请求首先到达接入层,此层若未能有效过滤无效流量,后端的业务服务和数据库将不堪重负。常见的方案是采用 “CDN + API网关” 的组合。

1.1 CDN静态加速与预热
将所有商品图片、活动页面等静态资源全量托管至CDN,利用其全球分布式节点实现就近访问,极大提升加载速度。同时,实施地域路由策略,让华北用户访问华北网关,华东用户访问华东网关,以降低网络延迟。

缓存策略至关重要:在活动开始前1小时,通过“预热”将静态页面推送到全国CDN节点;活动结束后,立即设置缓存过期,避免用户看到过时的库存信息。

1.2 网关精细化限流
使用如APISIX这类API网关,通过令牌桶等插件实现多维度的精细化限流,而非“一刀切”。例如:
- 单个IP地址每秒最多允许5个请求。
- 单个用户每分钟最多允许10次点击。
- 对未登录的请求直接拦截。

2. 流量削峰层:化解瞬时洪峰
秒杀流量往往在前几秒集中爆发,直接冲击数据库将导致系统崩溃。本层的核心作用是通过 “缓冲” 与 “排队” ,将瞬时尖峰流量平滑化。

2.1 消息队列缓冲
将用户的下单请求首先送入消息队列(如RocketMQ),后端服务根据自己的处理能力从容消费。消息队列起到了“减压阀”的作用。选择RocketMQ而非Kafka的重要原因在于其对事务消息的良好支持,更能满足业务一致性的需求。但需注意,请求入队仅表示进入排队,并非秒杀成功,需明确告知用户。

2.2 用户排队可视化
仅靠后台缓冲不够,还需让用户感知排队状态。可以利用Redis的List结构实现排队队列。
// 将用户ID加入特定商品的排队队列
LPUSH seckill:queue:{productId} {userId}
// 查询用户当前排队位置
LLEN seckill:queue:{productId}
根据队列长度和系统处理速度,前端可向用户反馈“当前排在第58位,预计等待2分钟”。这种透明化机制能显著减少用户因焦虑而产生的重复刷新请求。
2.3 积压应急处理
需要预先制定消息积压的应急预案。例如,正常情况下只开启一个消费组;当某个队列积压超过10万条时,自动扩容两个临时消费组进行分流,并暂停如“成功通知”等非核心消费,全力保障下单核心流程。

3. 业务逻辑层:微服务化与热点隔离
业务层是系统的“大脑”,需要兼顾效率、准确性与稳定性。可以采用Spring Cloud Alibaba作为微服务技术底座,整合了服务发现、熔断降级、RPC调用等能力。

服务拆分:将秒杀业务拆分为三个独立的微服务:
- 资格校验服务:负责校验用户登录状态、黑名单、参与次数等。
- 库存扣减服务:专注处理缓存预扣库存与数据库最终扣减逻辑。
- 订单生成服务:负责创建订单、调用支付等后续流程。

这种拆分实现了故障隔离,即使订单服务暂时不可用,前端的资格校验和库存扣减仍可正常运行。
本地缓存加速:将用户等级、历史参与记录等高频访问的资格数据存入Caffeine本地缓存,采用“用户ID+商品ID”作为Key,设置合理的过期时间,形成“本地缓存 → Redis → 数据库”的三级查询屏障,大幅降低平均响应耗时。
// 更新用户秒杀资格到本地缓存
caffeineCache.put(“user:12345:product:67890:qualification”, true);
// 从本地缓存查询资格
boolean hasQualification = caffeineCache.getIfPresent(“user:12345:product:67890:qualification”);
热点隔离:对于“1元秒杀iPhone”这类爆款商品,必须进行资源隔离。例如,为爆款商品单独部署50台服务器集群,而普通商品共享20台服务器,避免单一热点拖垮整个系统。

4. 数据层:高并发读写支撑
数据层是系统的“弹药库”,承担着最大的压力。Redis与MySQL需分工协作。
4.1 Redis集群存储热数据
实时库存、排队位置、资格结果等所有热数据均存入Redis集群。建议采用3主3从、16个分片的集群架构,理论上可支撑80万+ QPS。

4.2 MySQL分库分表存储冷数据
订单、支付记录等需持久化的数据存入MySQL。通过Sharding-JDBC等中间件,按商品ID哈希分8个库,每个库再分16张表,总计128张表,将单表数据量控制在百万级别,提升查询性能。

4.3 读写分离
配置MySQL主从复制,写操作(下单、扣库存)走主库,读操作(查询订单、支付状态)走从库。利用Sharding-JDBC自动路由,可降低主库40%的压力。但需注意主从同步有1-3秒延迟,前端需有相应提示。

关键细节:避开高频“深坑”
架构是骨架,细节决定成败。以下是秒杀系统中几个必须妥善处理的关键细节。
细节1:绝对防超卖方案
超卖的根源在于“读-判断-写”的非原子性操作。解决方案是 “Redis原子预扣 + MySQL乐观锁确认” 双重保障。
1.1 Redis Lua脚本原子预扣
在秒杀开始前,将库存加载至Redis。用户下单时,通过执行Lua脚本原子性地完成库存查询与扣减。
-- Lua脚本:原子扣减库存
local available = redis.call("HGET", KEYS[1], "available")
if not available or tonumber(available) < tonumber(ARGV[1]) then
return 0 -- 库存不足
end
redis.call("HINCRBY", KEYS[1], "available", -tonumber(ARGV[1]))
return 1 -- 扣减成功
1.2 MySQL乐观锁最终确认
Redis扣减成功后,需在数据库层面进行最终确认。在库存表中增加version字段,更新时比对版本号。
UPDATE seckill_stock
SET available_stock = available_stock - 1,
version = version + 1
WHERE product_id = #{productId}
AND available_stock >= 1
AND version = #{version}; -- 乐观锁控制
若此SQL影响行数为0,说明并发情况下已被其他请求抢先扣减,则需回滚Redis中的预扣库存。
细节2:缓存问题全解
缓存是性能加速器,但使用不当会引发穿透、击穿、雪崩三大问题。
2.1 缓存穿透
问题:查询一个不存在的数据(如无效商品ID),请求穿过缓存直击数据库。
方案:布隆过滤器 + 缓存空值。
- 将所有有效商品ID初始化到布隆过滤器,请求先经过滤器拦截。
- 对穿透的请求,在Redis中缓存一个空值(如
null)并设置短过期时间。

2.2 缓存击穿
问题:某个热点Key在失效瞬间,大量请求同时涌向数据库。
方案:分布式互斥锁 + 热点Key永不过期。
- 使用Redisson等工具实现分布式锁,只允许一个线程重建缓存。
- 对于爆款商品,不设置过期时间,而是通过后台定时任务异步更新缓存。
2.3 缓存雪崩
问题:大量缓存Key在同一时间点过期,导致所有请求落库。
方案:过期时间随机化 + 本地缓存兜底。
- 在基础过期时间上,增加一个随机偏移值(如±10分钟),分散失效时间。
- 应用层使用Caffeine本地缓存作为最后一道防线。


细节3:分布式最终一致性保障
分布式环境下追求强一致代价高昂,最终一致性是更务实的目标。
3.1 轻量级TCC事务
对于下单这类短流程,可采用Try-Confirm-Cancel模式:
- Try:检查资格,预扣Redis库存。
- Confirm:扣减MySQL库存,生成订单。
- Cancel:若失败,回滚Redis库存。
3.2 消息队列事务消息
利用RocketMQ的事务消息,确保“订单创建”与“库存扣减”的最终一致。订单服务发送半消息,库存消费成功后再确认,失败则进入死信队列人工处理。

3.3 实时对账任务
作为最后一道防线,设置定时任务每分钟对比Redis与MySQL的库存数据。若差值在合理延迟范围内(如±1)则忽略;若差值过大(如>10),则以MySQL为准修复Redis数据,确保数据最终一致。

优化方向:体验与成本的双赢
优化的核心是提升用户体验并控制成本。
优化1:多层无效请求过滤
据统计,秒杀中超过90%的请求是无效的(如未登录、重复提交、库存不足)。在接入层和应用层设置层层过滤规则,可提前拦截大部分无效流量,为系统大幅减负。
| 过滤阶段 |
规则示例 |
过滤效果 |
| 接入层 |
单IP频率限制、异常User-Agent识别 |
拦截约30%无效/恶意请求 |
| 应用层 |
校验登录态、黑名单、用户重复参与(用Redis Set记录) |
拦截约50%无效业务请求 |
优化2:非核心操作异步化
用户下单后,发送短信、App推送、更新积分等操作对实时性要求不高,完全可以异步处理。通过RocketMQ普通消息队列解耦,由后台服务集群根据自身负载能力逐步消费,提升核心链路响应速度。

优化3:基于流量的弹性伸缩
在云原生时代,手动扩容已不合时宜。利用Kubernetes的HPA(水平Pod自动伸缩)功能,可根据CPU/内存等指标自动扩容或缩容应用实例。例如,在秒杀开始前自动扩容至100个Pod,活动结束后自动缩容至8个,在保障性能的同时,显著节约资源成本。

总结:秒杀架构的核心心法
回顾整个设计过程,可以提炼出四个核心原则:
- 流量拦截前置化:将无效请求尽可能阻挡在系统上游(CDN、网关、排队),避免其消耗宝贵的后端资源。
- 数据一致底线化:通过Redis原子操作、MySQL乐观锁、TCC事务、实时对账构成多重保障,库存准确性是秒杀活动的生命线。
- 系统设计冗余化:缓存多层架构(本地+分布式)、服务拆分隔离、弹性伸缩,都是通过冗余来换取系统整体的高可用性。
- 监控与迭代常态化:没有一劳永逸的架构,必须建立完善的全链路监控、混沌工程演练和降级预案,并根据业务发展与流量变化持续优化。
希望这篇从实战角度剖析的秒杀架构设计,能为你提供清晰的思路和可落地的方案。在云栈社区中,你可以找到更多关于高并发系统设计与实战案例的深度讨论。