【 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. 降级方案
    • 活动过于火爆时展示友好提示
    • 关闭非核心服务保证秒杀进行

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

相关推荐
天然首长11 小时前
Redis相关
redis
小葛呀11 小时前
互联网大数据求职面试:从Zookeeper到数据挖掘的技术探讨
大数据·redis·zookeeper·面试·互联网·数据采集·技术栈
码码不爱我13 小时前
学习笔记:Redis入门
数据库·redis·学习
异常君18 小时前
Redis String 类型的底层实现与性能优化
java·redis·性能优化
ErizJ20 小时前
Redis高级|Redis单线程VS多线程(基础)
数据库·redis
小鸡脚来咯20 小时前
redis穿透、击穿、雪崩
数据库·redis·缓存
在未来等你1 天前
SQL进阶之旅 Day 29:NoSQL结合使用策略
redis·sql·mongodb·elasticsearch·postgresql·nosql·hybrid-database
运维老司机2 天前
Redis 安装实践:基于鲲鹏 ARM 架构 Ubuntu 环境
arm开发·redis·架构
不良手残2 天前
Redisson + Lettuce 在 Spring Boot 中的最佳实践方案
java·spring boot·redis·后端
明月看潮生2 天前
青少年编程与数学 01-011 系统软件简介 16 Redis数据库
数据库·redis·青少年编程·编程与数学