Spring Boot + Redis + RabbitMQ 高并发秒杀系统实战

简介: 本文基于生产级秒杀系统架构,从零搭建支撑万级QPS的高并发系统,融合Redis预扣库存(防超卖)、RabbitMQ异步削峰、MySQL最终落库及双重校验机制,并集成令牌桶限流与用户维度防刷,附完整可运行代码。

 💡 本文基于生产级秒杀系统架构,从零到一搭建一个支撑万级 QPS 的秒杀系统,涵盖 Redis 预扣库存、RabbitMQ 异步下单、分布式锁等核心技术,附完整代码。

一、项目背景与架构设计

秒杀系统的核心挑战是瞬时高并发超卖问题。传统数据库直击方案在万级 QPS 下会直接被打垮。本文采用以下架构:

用户请求 → Nginx → Spring Boot → Redis(预扣库存) → RabbitMQ(削峰) → MySQL(持久化)


核心设计思路

  1. Redis 预扣库存:利用 Redis 单线程特性保证原子性,避免超卖
  2. RabbitMQ 削峰:将同步下单转为异步,保护后端数据库
  3. 双重校验:Redis 预扣 + MySQL 事务确认,保证数据一致性

二、项目环境

组件

版本

Spring Boot

2.7.x

Redis

6.2

RabbitMQ

3.9

MySQL

8.0

三、核心代码实现

1. Maven 依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.2</version>
    </dependency></dependencies>

image.gif

2. Redis 配置类

@Configurationpublic class RedisConfig {    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);        
        // 使用 Jackson2JsonRedisSerializer 序列化 value
        Jackson2JsonRedisSerializer<Object> serializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
            ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);
        
        template.setValueSerializer(serializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();        return template;
    }
}

image.gif

3. 秒杀核心 Service(Redis 预扣库存)

@Service@Slf4jpublic class SeckillService {    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;    
    @Autowired
    private RabbitTemplate rabbitTemplate;    
    @Autowired
    private OrderMapper orderMapper;    
    // 商品库存 Key 前缀
    private static final String STOCK_KEY_PREFIX = "seckill:stock:";    // 已秒杀用户 Set Key 前缀
    private static final String USER_SET_PREFIX = "seckill:users:";    
    /**
     * 秒杀核心方法
     */
    public Result seckill(Long userId, Long goodsId) {        String stockKey = STOCK_KEY_PREFIX + goodsId;        String userSetKey = USER_SET_PREFIX + goodsId;        
        // 1. 校验用户是否已秒杀过(防止重复购买)
        Boolean isMember = redisTemplate.opsForSet().isMember(userSetKey, userId);        if (Boolean.TRUE.equals(isMember)) {            return Result.error("您已参与过该秒杀活动");
        }        
        // 2. Redis 预扣库存(原子操作)
        Long remaining = redisTemplate.opsForValue().decrement(stockKey);        if (remaining == null || remaining < 0) {            // 库存不足,回补
            redisTemplate.opsForValue().increment(stockKey);            return Result.error("秒杀失败,库存不足");
        }        
        // 3. 记录用户已秒杀
        redisTemplate.opsForSet().add(userSetKey, userId);        
        // 4. 发送消息到 RabbitMQ 异步下单
        SeckillMessage message = new SeckillMessage(userId, goodsId);
        rabbitTemplate.convertAndSend(            "seckill.exchange", 
            "seckill.route", 
            message
        );
        
        log.info("用户 {} 秒杀商品 {} 进入队列", userId, goodsId);        return Result.success("秒杀成功,订单处理中");
    }
}

image.gif

4. RabbitMQ 配置

@Configurationpublic class RabbitMQConfig {    
    // 交换机
    public static final String SECKILL_EXCHANGE = "seckill.exchange";    // 队列
    public static final String SECKILL_QUEUE = "seckill.queue";    // 路由键
    public static final String SECKILL_ROUTING_KEY = "seckill.route";    
    @Bean
    public DirectExchange seckillExchange() {        return new DirectExchange(SECKILL_EXCHANGE);
    }    
    @Bean
    public Queue seckillQueue() {        return QueueBuilder.durable(SECKILL_QUEUE)
            .withArgument("x-message-ttl", 60000)  // 消息60秒过期
            .withArgument("x-max-length", 100000)  // 最大队列长度
            .build();
    }    
    @Bean
    public Binding binding() {        return BindingBuilder.bind(seckillQueue())
            .to(seckillExchange())
            .with(SECKILL_ROUTING_KEY);
    }
}

image.gif

5. RabbitMQ 消费者(MySQL 落库)

@Component@Slf4jpublic class SeckillConsumer {    
    @Autowired
    private OrderMapper orderMapper;    
    @Autowired
    private GoodsMapper goodsMapper;    
    @RabbitListener(queues = "seckill.queue")
    public void handleSeckillMessage(SeckillMessage message, Channel channel, 
                                     @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws IOException {        try {            Long userId = message.getUserId();            Long goodsId = message.getGoodsId();            
            // 1. 数据库库存校验(兜底)
            Goods goods = goodsMapper.selectById(goodsId);            if (goods == null || goods.getStock() <= 0) {
                log.warn("商品 {} 库存不足", goodsId);
                channel.basicAck(deliveryTag, false);                return;
            }            
            // 2. 创建订单
            Order order = new Order();
            order.setUserId(userId);
            order.setGoodsId(goodsId);
            order.setStatus(0); // 待支付
            order.setCreateTime(LocalDateTime.now());
            orderMapper.insert(order);            
            // 3. 扣减数据库库存(乐观锁防止超卖)
            int updated = goodsMapper.decreaseStock(goodsId);            if (updated == 0) {
                log.warn("商品 {} 数据库扣库存失败", goodsId);
            }
            
            log.info("用户 {} 秒杀商品 {} 下单成功,订单ID: {}", 
                userId, goodsId, order.getId());            
            // 手动 ACK
            channel.basicAck(deliveryTag, false);
            
        } catch (Exception e) {
            log.error("处理秒杀消息失败", e);            // 重试3次后进入死信队列
            channel.basicNack(deliveryTag, false, true);
        }
    }
}

image.gif

6. 商品 Mapper(乐观锁扣库存)

@Mapperpublic interface GoodsMapper extends BaseMapper<Goods> {    
    @Update("UPDATE goods SET stock = stock - 1, " +
            "version = version + 1 WHERE id = #{goodsId} " +
            "AND stock > 0 AND version = #{version}")
    int decreaseStockWithVersion(@Param("goodsId") Long goodsId, 
                                 @Param("version") Integer version);    
    @Update("UPDATE goods SET stock = stock - 1 " +
            "WHERE id = #{goodsId} AND stock > 0")
    int decreaseStock(@Param("goodsId") Long goodsId);
}

image.gif

7. Controller 层

@RestController@RequestMapping("/seckill")@Slf4jpublic class SeckillController {    
    @Autowired
    private SeckillService seckillService;    
    @PostMapping("/{goodsId}")
    public Result doSeckill(@PathVariable Long goodsId, 
                            @RequestHeader("X-User-Id") Long userId) {        return seckillService.seckill(userId, goodsId);
    }    
    /**
     * 初始化库存到 Redis
     */
    @PostMapping("/init/{goodsId}")
    public Result initStock(@PathVariable Long goodsId) {        // 从数据库加载库存到 Redis
        // ...
        return Result.success("库存初始化完成");
    }
}

image.gif

四、关键优化点

1. 防止超卖的双重保障

// 第一层:Redis 原子递减Long remaining = redisTemplate.opsForValue().decrement(stockKey);// 第二层:MySQL 乐观锁int updated = goodsMapper.decreaseStockWithVersion(goodsId, version);

image.gif

2. 接口限流(基于 Redis 令牌桶)

public boolean tryAcquire(String key, int qps) {    String luaScript = 
        "local key = KEYS[1] " +        "local limit = tonumber(ARGV[1]) " +        "local current = tonumber(redis.call('get', key) or '0') " +        "if current + 1 > limit then " +        "   return 0 " +        "else " +        "   redis.call('incrby', key, 1) " +        "   redis.call('expire', key, 1) " +        "   return 1 " +        "end";    
    Long result = redisTemplate.execute(        new DefaultRedisScript<>(luaScript, Long.class),
        Collections.singletonList(key),
        String.valueOf(qps)
    );    return result != null && result == 1L;
}

image.gif

3. 防刷策略

// 用户维度限流:同一用户5秒内只能请求1次String userLimitKey = "seckill:limit:" + userId;if (redisTemplate.opsForValue().setIfAbsent(userLimitKey, "1", 5, TimeUnit.SECONDS)) {    // 允许请求} else {    return Result.error("操作过于频繁,请稍后再试");
}

image.gif

五、性能压测数据

使用 JMeter 进行压测,模拟 10000 并发:

方案

QPS

平均响应时间

错误率

直击数据库

800

1200ms

35%

Redis 预扣+MQ

12000

15ms

0%

六、生产环境踩坑总结

⚠️ 踩坑1:Redis 与数据库库存不一致

解决方案:定时任务对账,以数据库为准修复 Redis 库存

⚠️ 踩坑2:RabbitMQ 消息堆积

解决方案:增加消费者实例数 + 设置队列 TTL 和死信队列

⚠️ 踩坑3:用户重复秒杀

解决方案:Redis Set 记录已参与用户 + 数据库唯一索引兜底

七、完整项目结构

src/main/java/com/example/seckill/
├── config/
│   ├── RedisConfig.java
│   └── RabbitMQConfig.java
├── controller/
│   └── SeckillController.java
├── service/
│   └── SeckillService.java
├── mq/
│   └── SeckillConsumer.java
├── mapper/
│   ├── GoodsMapper.java
│   └── OrderMapper.java
└── entity/
    ├── Goods.java
    ├── Order.java
    └── SeckillMessage.java

image.gif

本文由 摸鱼不慌 发布,转载请注明出处。

文章链接:生产级实战:基于Redis Lua的分布式幂等框架设计与实现 - 摸鱼不慌

目录
相关文章
|
21天前
|
存储 监控 API
基于 RAG + LangChain 搭建企业级私有知识库问答系统(2026 实战版)
本文是作者基于多个企业RAG知识库落地经验的实战总结,提供完整可运行代码与十年避坑指南。涵盖文档解析、混合检索、向量存储、DeepSeek接入、结果重排、拒答机制及效果评估,助你构建本地可运行、生产可扩展的企业级私有知识库系统。(239字)
313 1
|
22天前
|
人工智能 JSON NoSQL
从零构建 AI Agent:基于 LangGraph 的多工具智能体实战(含完整代码)
本文详解如何用LangGraph从零构建生产级AI Agent:支持自主规划、多工具调用(天气/搜索/计算/笔记)、失败重试与Redis会话记忆。代码开箱即用,涵盖架构设计、状态图实现及流式输出等核心能力,助企业突破RAG局限,落地真实业务场景。
219 0
|
6月前
|
人工智能 Linux API
【最全】零基础玩转OpenClaw:阿里云/本地部署步骤+Skill清单+免费API配置一站式教程
OpenClaw(原Clawdbot)并非简单的AI聊天机器人,而是一套具备任务自动化、跨平台协作能力的AI操作系统,Skill作为其核心扩展能力,能让AI真正实现“动手干活”。2026年的OpenClaw生态已拥有3000+Skill,覆盖30余个应用领域,无论是个人提升办公效率,还是团队实现轻量协作,都能通过合理配置满足需求。本文将从零基础部署入手,完整讲解阿里云、MacOS、Linux、Windows11的OpenClaw部署流程,搭配阿里云百炼API配置方法、必装Skill清单及常见问题解答,让新手也能快速上手,真正让AI落地工作场景。
841 3
|
23天前
|
缓存 运维 架构师
基于 RAG + LangChain + FastAPI 搭建生产级私有知识库问答系统(完整可运行)
本文以十年架构师视角,详解如何用RAG解决大模型落地三大痛点:知识滞后、私有文档不可读、幻觉。提供一套生产级、可运行、可扩展的FastAPI+LangChain+Chroma后端方案,含文档解析、智能分块、混合检索、重排、缓存与评估,代码经实测,开箱即用。(239字)
222 4
|
1月前
|
人工智能 编解码 安全
英特尔中国加速适配国产AI模型MiniMax H3布局本地化部署
英特尔锐炫Pro B70完成对国产开源视频模型MiniMax H3的Day 0适配,支持15秒2K+立体声视频生成。依托32GB显存、367 TOPS算力及8卡混合并行方案,实现开箱即用、低成本本地部署,加速AI视频商业化落地。(239字)
211 1
|
存储 JSON API
SpringBoot3集成ElasticSearch
SpringBoot3集成ElasticSearchElasticsearch是一个分布式、RESTful风格的搜索和数据分析引擎,适用于各种数据类型,数字、文本、地理位置、结构化数据、非结构化数据;
2108 0
|
24天前
|
缓存 API 异构计算
基于 LangChain + 通义千问 + Chroma 的本地知识库 RAG 问答系统:从零到生产的完整实战
本文提供一套开箱即用的RAG企业级知识库系统,基于LangChain 0.x + Chroma + 通义千问qwen-turbo,覆盖文档加载、切分、向量化、检索、生成全链路,并集成查询改写、LLM兜底、文件溯源、流式输出等工程能力,代码模块清晰,可直接落地生产。(239字)
150 0
|
运维 监控 关系型数据库
AI 时代的 MySQL 数据库运维解决方案
本文探讨了大模型与MySQL数据库运维结合所带来的变革,介绍了构建结构化运维知识库、选择合适的大模型、设计Prompt调用策略、开发MCP Server以及建立监控优化闭环等关键步骤。通过将自然语言处理能力与数据库运维相结合,实现了故障智能诊断、SQL自动优化等功能,显著提升了MySQL运维效率和准确性。
1084 18
|
6月前
|
人工智能 JavaScript API
1个人=1个团队!OpenClaw打造一人公司:阿里云/本地搭建16个AI员工+百炼API配置,实现AI工作流自动化!
2026年的创业生态里,“一人公司”的终极形态已然到来——凭借AI工具的赋能,一个人就能掌控16个AI员工,实现从内容创作、客户运营到数据分析、财务管理的全流程公司运转。BuildShip创始人Vadim正是这一模式的实践者,他通过AI Agent工作流自动化,让16个不同分工的AI员工各司其职,自己则成为核心的战略决策者,将个人效率放大到极致。而OpenClaw(Clawdbot)作为开源且功能强大的AI生产力工具,成为搭建个性化AI Agent、落地一人公司模式的核心载体。本文将拆解16个AI员工的精准分工与优化方法,详解基于OpenClaw搭建AI工作流的实操逻辑,同时完整整理2026年
2177 3

热门文章

最新文章