【 Redis | 实战篇 秒杀优化 】

Redis 秒杀优化实战

秒杀场景的挑战

秒杀活动是高并发场景的典型代表,主要面临以下挑战:

  1. 高并发访问:短时间内大量用户涌入
  2. 库存超卖:库存扣减的原子性问题
  3. 系统性能:数据库压力过大
  4. 公平性:防止机器人抢购

Redis 优化方案

1. 页面静态化 + CDN 加速

java 复制代码
// 使用 Thymeleaf 或其他模板引擎提前生成静态页面
@GetMapping("/seckill/item/{id}")
public String getSeckillItem(@PathVariable Long id, Model model) {
    // 从Redis获取商品信息
    SeckillItem item = redisService.get("seckill:item:" + id);
    model.addAttribute("item", item);
    return "seckill_item_static";
}

2. Redis 预减库存

java 复制代码
// 活动开始前将库存加载到Redis
public void loadSeckillItemToRedis(Long itemId, Integer stock) {
    String key = "seckill:stock:" + itemId;
    redisTemplate.opsForValue().set(key, stock);
}

3. 内存标记 + Redis 原子操作

java 复制代码
// 使用本地内存标记减少Redis访问
private Map<Long, Boolean> localOverMap = new ConcurrentHashMap<>();

public boolean seckill(Long userId, Long itemId) {
    // 1. 检查内存标记
    if (localOverMap.get(itemId) != null) {
        return false;
    }
    
    // 2. Redis预减库存
    Long stock = redisTemplate.opsForValue().decrement("seckill:stock:" + itemId);
    if (stock == null || stock < 0) {
        localOverMap.put(itemId, true);
        return false;
    }
    
    // 3. 进入下单队列
    SeckillMessage message = new SeckillMessage(userId, itemId);
    rabbitTemplate.convertAndSend("seckill.exchange", "seckill.routingKey", message);
    
    return true;
}

4. Lua 脚本保证原子性

lua 复制代码
-- seckill.lua
local userId = KEYS[1]
local itemId = KEYS[2]
local stockKey = "seckill:stock:" .. itemId
local orderKey = "seckill:order:" .. itemId

-- 检查库存
local stock = tonumber(redis.call('get', stockKey))
if stock <= 0 then
    return 0
end

-- 检查是否已购买
if redis.call('sismember', orderKey, userId) == 1 then
    return 1
end

-- 扣减库存并记录订单
redis.call('decr', stockKey)
redis.call('sadd', orderKey, userId)
return 2

Java 调用 Lua 脚本:

java 复制代码
public class SeckillScript {
    private static final String SECKILL_SCRIPT =
        "local userId = KEYS[1]\n" +
        "local itemId = KEYS[2]\n" +
        // ... 上面Lua脚本内容
        
    public static final RedisScript<Long> script = 
        new DefaultRedisScript<>(SECKILL_SCRIPT, Long.class);
}

// 使用脚本
Long result = redisTemplate.execute(
    SeckillScript.script,
    Arrays.asList(userId.toString(), itemId.toString())
);

5. 消息队列异步处理

java 复制代码
@RabbitListener(queues = "seckill.queue")
public void processSeckillOrder(SeckillMessage message) {
    Long userId = message.getUserId();
    Long itemId = message.getItemId();
    
    // 1. 数据库校验库存
    SeckillItem item = seckillItemMapper.selectById(itemId);
    if (item.getStock() <= 0) {
        return;
    }
    
    // 2. 检查是否重复购买
    SeckillOrder exist = seckillOrderMapper.findByUserIdAndItemId(userId, itemId);
    if (exist != null) {
        return;
    }
    
    // 3. 创建订单
    createOrder(userId, item);
}

6. 分布式锁防止重复提交

java 复制代码
public boolean trySeckillLock(Long userId, Long itemId) {
    String lockKey = "seckill:lock:" + userId + ":" + itemId;
    String value = UUID.randomUUID().toString();
    
    Boolean success = redisTemplate.opsForValue().setIfAbsent(
        lockKey, value, 10, TimeUnit.SECONDS);
    
    if (Boolean.TRUE.equals(success)) {
        try {
            // 执行业务逻辑
            return true;
        } finally {
            // 释放锁时要验证value,防止误删其他线程的锁
            if (value.equals(redisTemplate.opsForValue().get(lockKey))) {
                redisTemplate.delete(lockKey);
            }
        }
    }
    return false;
}

完整优化流程

  1. 系统初始化:将秒杀商品库存加载到Redis
  2. 请求入口
    • 先查内存标记 → 快速失败
    • 执行Lua脚本 → 原子性操作
  3. 异步下单
    • 通过消息队列削峰
    • 后台服务处理订单
  4. 结果返回
    • 轮询查询订单结果
    • 或使用WebSocket推送结果

监控与降级

  1. Redis监控:监控QPS、内存使用、连接数等
  2. 限流措施
    • 接口限流(如Guava RateLimiter)
    • Nginx层限流
  3. 降级方案
    • 活动过于火爆时展示友好提示
    • 关闭非核心服务保证秒杀进行

通过以上优化,可以极大提升秒杀系统的性能和可靠性,轻松应对高并发场景。

相关推荐
搞不懂语言的程序员1 小时前
如何设计一个二级缓存(Redis+Caffeine)架构?Redis 6.0多线程模型如何工作?
redis·架构·wpf
YJQ99673 小时前
Redis配置与优化:提升NoSQL数据库性能的关键策略
数据库·redis·nosql
Bug退退退1234 小时前
初识 Redis
redis
嘵奇7 小时前
Spring Boot中Redis序列化配置详解
spring boot·redis·后端
vvilkim13 小时前
深度解析:Redis 性能优化全方位指南
数据库·redis·性能优化
羊羊羊i13 小时前
Redis进阶知识
数据库·redis·缓存
jjkkzzzz21 小时前
Linux下的c/c++开发之操作Redis数据库
数据库·c++·redis
my_styles1 天前
docker-compose部署项目(springboot服务)以及基础环境(mysql、redis等)ruoyi-ry
spring boot·redis·后端·mysql·spring cloud·docker·容器