
目录一、整体业务架构分层分布式拆分1. 微服务拆分2. 完整秒杀请求链路3. 核心技术解决的痛点二、多级缓存设计三层缓存层级 1网关本地缓存 Caffeine堆内存缓存层级 2应用本地缓存 Caffeine每个微服务实例本地缓存层级 3Redis 分布式缓存核心缓存层缓存预热关键前置操作三、Redis Lua 脚本原子扣减库存核心防超卖3.1 为什么用 Lua3.2 Lua 脚本功能一次性完成 5 个校验Lua 完整脚本代码3.3 Java 调用 Lua 脚本Redisson 实现3.4 库存数据分层四、RocketMQ 异步削峰下单分布式解耦4.1 为什么用 MQ4.2 消息模型事务消息 幂等消费1. 生产者流程秒杀服务2. 消费者流程订单服务独立部署3. 延迟消息回滚库存4.3 MQ 可靠性保障五、分布式架构配套核心解决方案5.1 Gateway 网关限流第一层流量拦截5.2 分布式防重复下单5.3 缓存穿透 / 击穿 / 雪崩解决方案5.4 分布式锁补充场景5.5 MySQL 数据库优化六、完整请求代码流程演示1. 网关层过滤伪代码2. 秒杀接口核心 Controller3. RocketMQ 事务生产者核心4. 订单消费者异步落库七、高并发压力测试 问题兜底方案1. 压测指标2. 异常兜底机制八、架构优缺点总结优势可扩展优化点整体架构微服务分布式 多级缓存防击穿 RedisLua 原子扣库存 RocketMQ 异步削峰下单 分布式锁 库存一致性兜底技术栈SpringCloud Alibaba (NacosOpenFeignSentinel) Redis Lua RocketMQ MySQL MyBatis-Plus Redisson一、整体业务架构分层分布式拆分1. 微服务拆分gateway 网关服务限流、路由、请求鉴权、黑名单、秒杀流量初步拦截seckill-goods 商品秒杀服务商品基础信息、库存预热、库存兜底、订单校验seckill-order 秒杀订单服务异步创建订单、订单状态流转、取消超时订单seckill-user 用户服务用户登录、资格校验、限购校验seckill-stock 库存服务Redis 库存操作、库存同步 MySQL、库存预警2. 完整秒杀请求链路用户 → Gateway 网关限流 → Nacos 配置中心拉取秒杀配置 → 多级缓存本地 Caffeine Redis 分布式缓存查商品库存信息 → RedisLua 脚本原子扣减预库存防超卖→ 发送下单消息至 RocketMQ异步削峰→ 直接返回用户「排队中抢购成功」→ 消费者异步创建 MySQL 订单、扣减真实数据库库存 → 定时任务校验超时未支付订单回滚 Redis 预库存 MySQL 库存3. 核心技术解决的痛点高并发流量打垮 DB多级缓存 MQ 异步削峰DB 只处理最终订单超卖问题Lua 脚本单线程原子操作库存杜绝并发扣减错乱缓存击穿 / 穿透 / 雪崩本地缓存 布隆过滤器 过期时间随机化分布式重复下单Redis 分布式防重令牌超量抢购每人限购 N 件Lua 内同时校验限购消息丢失 / 重复消费RocketMQ 事务消息 幂等消费二、多级缓存设计三层缓存层级 1网关本地缓存 Caffeine堆内存缓存作用拦截大量重复商品查询请求不进后端微服务减轻 Redis 压力缓存内容秒杀商品基础信息名称、图片、价格、活动时间过期策略秒杀开始前 5 分钟加载活动结束自动失效随机过期 ±30s 防雪崩配合 Sentinel 网关限流未登录用户、黑名单、频率限制直接在网关拦截层级 2应用本地缓存 Caffeine每个微服务实例本地缓存每个秒杀服务节点本地缓存商品元数据减少 Redis 网络 IO// Caffeine 本地缓存配置 Bean public CacheString, SeckillGoodsDTO seckillLocalCache() { return Caffeine.newBuilder() .maximumSize(10000) .expireAfterWrite(5, TimeUnit.MINUTES) .recordStats() .build(); }查询逻辑先查本地 Caffeine 缓存命中直接返回未命中查询 Redis 分布式缓存写入本地缓存再返回Redis 无数据再查 MySQL回填两级缓存层级 3Redis 分布式缓存核心缓存层Redis 存储三大核心数据商品基础信息 Hashseckill:goods:{goodsId}可秒杀库存 Stringseckill:stock:{goodsId}预扣库存用户限购 Setseckill:buy:{goodsId}存放已下单用户 ID限制每人限购 1 件防重令牌seckill:token:{userId}:{goodsId}防止重复提交布隆过滤器seckill:bloom过滤不存在的商品 ID解决缓存穿透缓存预热关键前置操作秒杀活动开启前定时任务预热缓存从 MySQL 批量查询秒杀商品写入 Redis Hash 商品信息、库存数值加载到各微服务 Caffeine 本地缓存初始化布隆过滤器存入所有有效秒杀商品 ID三、Redis Lua 脚本原子扣减库存核心防超卖3.1 为什么用 LuaRedis 单线程执行 Lua 脚本多条库存操作原子化中间不会被其他命令插入完美解决并发超卖 如果分开多条 Redis 命令getdecr高并发下会出现竞态条件库存扣为负数。3.2 Lua 脚本功能一次性完成 5 个校验脚本入参KEYS [1]库存 keyseckill:stock:{goodsId}KEYS [2]用户限购集合 keyseckill:buy:{goodsId}KEYS [3]防重令牌 keyseckill:token:{userId}:{goodsId}ARGV [1]用户 IDARGV [2]每人最大购买数量默认 1ARGV [3]令牌过期时间执行逻辑判断防重令牌是否存在存在则返回重复提交判断用户已购数量是否达到限购上限达到返回限购失败获取当前剩余库存库存 0 返回抢购失败原子库存 - 1将用户 ID 加入限购集合设置防重令牌返回 0抢购成功负数对应失败码Lua 完整脚本代码lua-- 1. 校验重复提交令牌 local token redis.call(GET, KEYS[3]) if token then return -1 -- 重复提交 end -- 2. 校验用户限购 local buyCount redis.call(SCARD, KEYS[2]) if tonumber(buyCount) tonumber(ARGV[2]) then return -2 -- 超出限购 end -- 3. 校验库存 local stock tonumber(redis.call(GET, KEYS[1])) if stock 0 then return -3 -- 库存不足 end -- 4. 原子扣减库存 redis.call(DECR, KEYS[1]) -- 5. 记录已购用户 防重令牌 redis.call(SADD, KEYS[2], ARGV[1]) redis.call(SET, KEYS[3], 1, EX, ARGV[3]) return 0 -- 抢购成功3.3 Java 调用 Lua 脚本Redisson 实现Autowired private RedissonClient redissonClient; // 预扣库存返回状态码 public int deductStock(Long goodsId, Long userId) { String stockKey seckill:stock: goodsId; String userBuyKey seckill:buy: goodsId; String tokenKey seckill:token: userId : goodsId; ListString keys Arrays.asList(stockKey, userBuyKey, tokenKey); // 参数userId、限购1件、令牌有效期60s ListObject args Arrays.asList(userId.toString(), 1, 60); String luaScript 上面完整lua脚本字符串; RScript script redissonClient.getScript(); Long result script.eval(RScript.Mode.READ_WRITE, luaScript, RScript.ReturnType.INTEGER, keys, args.toArray()); return result.intValue(); }3.4 库存数据分层Redis 预库存高并发操作用户抢购只操作 RedisMySQL 真实库存最终一致性MQ 消费者异步扣减兜底方案定时任务对比 Redis 预库存与 MySQL 库存不一致自动修复四、RocketMQ 异步削峰下单分布式解耦4.1 为什么用 MQ秒杀瞬时流量几万 QPS同步创建订单会直接打崩 MySQL 通过 RocketMQ 异步削峰Redis 预扣库存成功后直接返回前端订单落库交给消费者异步处理。4.2 消息模型事务消息 幂等消费1. 生产者流程秒杀服务Lua 返回抢购成功后构建秒杀消息体用户 ID、商品 ID、活动 ID、唯一消息流水号 msgId发送 RocketMQ 事务消息事务本地记录「预下单记录」标记待支付提交事务失败则回滚 Redis 库存消息实体Data public class SeckillOrderMsg { private Long msgId; // 全局唯一消息ID用于幂等 private Long userId; private Long goodsId; private LocalDateTime createTime; }2. 消费者流程订单服务独立部署监听 Topicseckill_order_topic开启批量消费、幂等处理 幂等保障消费前根据 msgId 查询本地消费记录表已消费直接跳过MySQL 订单表唯一索引user_id goods_id防止重复订单消费逻辑校验商品活动是否有效、用户资格插入 MySQL 秒杀订单状态 待支付扣减 MySQL 真实商品库存发送延迟消息30 分钟用于超时未支付取消订单3. 延迟消息回滚库存RocketMQ 延迟级别设置 30min用户 30 分钟未支付 → 延迟消息触发删除订单、MySQL 库存 1Lua 脚本恢复 Redis 预库存、移除用户限购记录、清除防重令牌4.3 MQ 可靠性保障生产者事务消息保证 Redis 扣库存和发消息原子一致Broker开启同步刷盘防止消息丢失消费者手动 ACK业务执行成功再确认失败重试 3 次死信队列存储异常订单人工处理五、分布式架构配套核心解决方案5.1 Gateway 网关限流第一层流量拦截使用 SpringCloud Gateway Sentinel 实现多维度限流IP 限流单 IP 每秒最多 5 次秒杀请求用户限流同一用户 1 分钟仅允许发起 3 次抢购全局限流秒杀接口 QPS 上限 10000超出直接返回「活动火爆请重试」时间拦截非秒杀时间段直接拒绝请求5.2 分布式防重复下单双重防重Lua 脚本内 Redis 令牌防 60s 内重复提交MQ 消费端基于 msgId 幂等 数据库唯一索引5.3 缓存穿透 / 击穿 / 雪崩解决方案缓存穿透不存在的商品大量查询Redis 布隆过滤器预存所有秒杀商品 ID不存在直接拦截MySQL 空结果写入 Redis 空缓存过期 30s缓存击穿热点商品 key 过期瞬间并发永不过期热点秒杀商品缓存Redisson 分布式互斥锁过期重建缓存只放行一个请求缓存雪崩大量 key 同时过期缓存过期时间统一增加随机偏移量 ±30~120s多级缓存兜底本地 Caffeine 不受 Redis 过期影响5.4 分布式锁补充场景缓存重建分布式锁定时库存校对锁防止多实例同时修正库存活动配置修改分布式锁5.5 MySQL 数据库优化秒杀订单分表按 user_id 哈希分 16 张订单表降低单表压力商品库存表单独拆分seckill_stock不和普通商品共用表核心字段建立索引goods_id、user_id、order_status关闭不必要事务、减少锁粒度更新库存使用乐观锁sql-- 乐观锁扣减真实库存防止并发更新超卖 UPDATE seckill_stock set stock stock - 1 where goods_id ? and stock 0;六、完整请求代码流程演示1. 网关层过滤伪代码// Gateway全局过滤器 Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { String path exchange.getRequest().getPath().value(); if (path.contains(/seckill/do)) { // 1. 判断是否登录 // 2. Sentinel限流 // 3. Caffeine本地缓存商品信息快速返回 } return chain.filter(exchange); }2. 秒杀接口核心 ControllerRestController RequestMapping(/seckill) public class SeckillController { Autowired private SeckillService seckillService; PostMapping(/do) public Result? seckill(RequestParam Long goodsId, RequestParam Long userId) { // 1. 本地缓存查询商品活动是否开启 SeckillGoodsDTO goods localCache.getIfPresent(goods: goodsId); if (goods null) { goods seckillService.getGoodsFromRedis(goodsId); localCache.put(goods: goodsId, goods); } // 校验活动时间 if (!seckillService.checkActivityTime(goods)) { return Result.fail(未到秒杀时间); } // 2. Lua原子预扣库存 int code seckillService.deductStock(goodsId, userId); if (code ! 0) { return switch (code) { case -1 - Result.fail(请勿重复抢购); case -2 - Result.fail(每人限购1件); case -3 - Result.fail(库存不足); default - Result.fail(抢购失败); }; } // 3. 发送RocketMQ异步下单消息 SeckillOrderMsg msg seckillService.buildOrderMsg(goodsId, userId); seckillService.sendSeckillTransactionMsg(msg); // 直接返回排队成功前端轮询订单状态 return Result.success(抢购成功订单创建中); } }3. RocketMQ 事务生产者核心Component public class SeckillMQProducer { Autowired private TransactionMQProducer transactionMQProducer; public void sendSeckillTransactionMsg(SeckillOrderMsg msg) throws Exception { Message message new Message(seckill_order_topic, JSON.toJSONBytes(msg)); // 发送事务消息绑定本地事务监听器 transactionMQProducer.sendMessageInTransaction(message, msg); } }4. 订单消费者异步落库Component RocketMQMessageListener(topic seckill_order_topic, consumerGroup seckill_order_consumer_group) public class SeckillOrderConsumer implements RocketMQListenerSeckillOrderMsg { Autowired private SeckillOrderService orderService; Override public void onMessage(SeckillOrderMsg msg) { // 幂等校验 if (orderService.existsMsgId(msg.getMsgId())) { return; } // 创建订单、扣减MySQL库存 orderService.createSeckillOrder(msg); // 发送30分钟延迟消息处理超时未支付 orderService.sendDelayCancelMsg(msg); // 记录消费日志保证幂等 orderService.saveConsumeRecord(msg.getMsgId()); } }七、高并发压力测试 问题兜底方案1. 压测指标单机 Redis 支持 5w QPS 库存操作Gateway 网关单机承载 1w 并发请求MQ 异步后 MySQL 每秒仅处理几百条订单写入2. 异常兜底机制Redis 宕机降级Sentinel 熔断直接返回活动拥挤不穿透到 DBMQ 消息堆积扩容消费者、增加分区、批量消费库存不一致定时任务每 5 分钟校对 Redis 与 MySQL 库存自动补偿差额未支付订单定时清理延迟消息 定时任务双重兜底防止库存永久锁定超卖应急Lua 脚本严格控制预库存MySQL 乐观锁二次拦截双重保障杜绝超卖八、架构优缺点总结优势多级缓存层层拦截流量DB 压力极小Lua 原子操作彻底解决超卖无并发安全问题RocketMQ 异步削峰支持百万级瞬时并发分布式微服务横向扩容网关、库存、订单服务可独立扩容完整幂等、限流、缓存防护、数据兜底体系生产可用可扩展优化点Redis 集群主从 哨兵实现高可用商品库存分片 Redis拆分热点商品 key 分散压力前端页面静态化 CDN进一步降低后端请求量接入分布式 ID雪花算法生成订单号、消息 ID秒杀活动冷热数据分离历史订单归档至分库分表