Redis+Lua+RabbitMQ:黑马点评秒杀系统设计实战

一、秒杀场景的核心挑战

在开发黑马点评的秒杀模块时,我们面临着典型的高并发系统设计难题。以"限量特价套餐秒杀"活动为例,1000份商品在活动开始后3秒内就被抢购一空,系统峰值QPS达到5000+。

1.1 秒杀场景三大难题

1. 超卖问题
数据库库存减到负数,出现"卖了1000件商品却产生1200个订单"的情况

2. 高并发冲击
瞬时流量是平时的100倍以上,直接击穿数据库连接池

3. 恶意请求泛滥
脚本抢购、刷单等行为占用系统资源

二、技术方案设计与实现

2.1 整体架构设计

采用分层削峰策略:

  1. 前端:静态资源CDN + 按钮防重复点击

  2. 网关层:限流熔断

  3. 服务层:Redis库存校验 + 异步下单

  4. 持久层:数据库最终一致性

2.2 Redis库存预减

关键代码实现:

// 初始化库存到Redis
public void initSeckillStock(Long seckillId, int stock) {
    String key = "seckill:stock:" + seckillId;
    stringRedisTemplate.opsForValue().set(key, String.valueOf(stock));
}

// Lua脚本保证原子性
private static final String STOCK_DEDUCTION_SCRIPT =
    "if tonumber(redis.call('get', KEYS[1])) > 0 then\n" +
    "    redis.call('decr', KEYS[1])\n" +
    "    return 1\n" +
    "else\n" +
    "    return 0\n" +
    "end";

2.3 RabbitMQ异步削峰

消息队列配置:

@Configuration
public class RabbitMQConfig {
    
    @Bean
    public Queue seckillQueue() {
        return new Queue("seckill.queue", true);
    }
    
    @Bean
    public Exchange seckillExchange() {
        return new DirectExchange("seckill.exchange");
    }
    
    @Bean
    public Binding binding() {
        return BindingBuilder.bind(seckillQueue())
               .to(seckillExchange()).with("seckill.routingKey").noargs();
    }
}

下单流程改造:

public Result handleSeckill(Long userId, Long seckillId) {
    // 1. Redis校验库存
    Long result = stringRedisTemplate.execute(
        new DefaultRedisScript<>(STOCK_DEDUCTION_SCRIPT, Long.class),
        Collections.singletonList("seckill:stock:" + seckillId));
    
    // 2. 库存不足
    if (result == 0) {
        return Result.fail("秒杀已结束");
    }
    
    // 3. 发送MQ消息
    SeckillMessage message = new SeckillMessage(userId, seckillId);
    rabbitTemplate.convertAndSend("seckill.exchange", 
                                "seckill.routingKey", 
                                message);
    return Result.ok("排队中");
}

三、性能优化成果

通过JMeter压测对比(模拟10000并发):

指标原始方案优化方案
峰值QPS5003200
平均响应时间1200ms180ms
数据库CPU使用率95%30%
订单创建成功率68%99.9%

关键提升点:

  1. Redis承担了99%的读请求

  2. 数据库写入量降低到原来的1/20

  3. 系统吞吐量提升6倍

四、安全防护措施

4.1 限流策略实现

网关层限流:

@Bean
public KeyResolver ipKeyResolver() {
    return exchange -> Mono.just(
        exchange.getRequest().getRemoteAddress().getHostName());
}

@Bean
public RedisRateLimiter redisRateLimiter() {
    return new RedisRateLimiter(100, 200);
}

接口级别限流:

@RateLimiter(value = 100, key = "#seckillId")
public Result querySeckillInfo(Long seckillId) {
    // 查询逻辑
}

4.2 防刷策略

  1. 验证码机制:秒杀前需要完成图形验证

  2. 用户限购:Redis记录用户购买标记

    redis

    SETEX seckill:user:{userId}:{seckillId} 3600 1
  3. IP黑名单:Nginx限制单个IP访问频率

五、延伸思考与优化方向

  1. 库存预热优化

    • 采用分段库存方案(如将1000库存分为10个100)

    • 使用Redis Cluster分散热点key压力

  2. 订单处理优化

    /* 使用乐观锁防止超卖 */
    UPDATE seckill_goods 
    SET stock = stock - 1 
    WHERE id = #{id} AND stock > 0
  3. 数据一致性保障

    • 引入定时任务补偿机制

    • 建立死信队列处理异常订单

六、经验总结

  1. 避免过早优化:先确保功能正确性,再考虑性能

  2. 监控必不可少

    • Redis内存/CPU监控

    • RabbitMQ堆积告警

    • 数据库慢查询监控

  3. 降级方案:准备静态化降级页面应对极端情况

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值