Java 高并发 + Redis 缓存击穿、雪崩解决方案

🚀 引言

在高并发系统中,Redis 作为缓存层,大幅度提升了查询速度,减轻了数据库压力。但是,在特殊情况下,Redis 可能会失效,导致大量请求直接打到数据库,造成系统崩溃。

本篇文章将深入解析缓存穿透、缓存击穿和缓存雪崩的问题,并提供实际解决方案和代码示例,助你打造高可用的缓存架构。


🔥 1. 常见缓存问题

📌 1.1 缓存穿透

现象:查询一个数据库中不存在的数据,导致请求绕过缓存直接访问数据库,形成 数据库级 DDoS
示例:用户请求 id=-1 或 id=99999999 的数据,而数据库中并没有该记录。

解决方案

  • 布隆过滤器(Bloom Filter)
  • 缓存空值
// 使用 Guava 创建布隆过滤器,存储数据库中已有的 key
BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), 1000000);
if (!bloomFilter.mightContain(id)) {
    return null; // 直接返回,避免查询数据库
}

📌 1.2 缓存击穿

现象:某个热点 key 过期的瞬间,大量请求涌入,导致数据库瞬时压力巨大。
示例:某明星的微博详情页数据,缓存过期后,短时间内数百万请求直接打到数据库。

解决方案

  • 设置热点数据永不过期
  • 加互斥锁
  • 异步重建缓存
// Redis 分布式锁
String lockKey = "lock:product:" + productId;
boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if (!lock) {
    return "系统繁忙,请稍后再试"; // 只允许一个线程更新缓存
}
try {
    Product product = db.getProductById(productId); // 查询数据库
    redisTemplate.opsForValue().set("product:" + productId, product, 30, TimeUnit.MINUTES);
} finally {
    redisTemplate.delete(lockKey); // 释放锁
}

📌 1.3 缓存雪崩

现象:大量缓存数据同时失效,导致数据库瞬间扛不住,系统崩溃。
示例:凌晨 0 点,所有缓存数据统一过期,导致数据库负载飙升。

解决方案

  • 设置缓存随机过期时间,避免同时失效
  • Redis 主从架构 + 哨兵
  • 限流 + 降级
// 设置随机过期时间,防止缓存同时失效
int expireTime = 60 * 60 + new Random().nextInt(300); // 60 分钟 + 0~5 分钟随机时间
redisTemplate.opsForValue().set("user:" + userId, user, expireTime, TimeUnit.SECONDS);

🛠 2. 代码实战:Spring Boot + Redis

完整实现

@RestController
@RequestMapping("/cache")
public class CacheController {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ProductService productService;

    private static final String LOCK_PREFIX = "lock:product:";

    @GetMapping("/product/{id}")
    public Product getProduct(@PathVariable("id") Integer id) {
        String key = "product:" + id;

        // 先查询 Redis
        String productJson = redisTemplate.opsForValue().get(key);
        if (productJson != null) {
            return JSON.parseObject(productJson, Product.class);
        }

        // 加锁防止缓存击穿
        String lockKey = LOCK_PREFIX + id;
        boolean isLock = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
        if (!isLock) {
            return null; // 其他线程正在更新缓存
        }

        try {
            // 查询数据库
            Product product = productService.getProductById(id);
            if (product == null) {
                redisTemplate.opsForValue().set(key, "", 60, TimeUnit.SECONDS); // 缓存空值
                return null;
            }

            // 设置缓存,并加随机过期时间
            int expireTime = 3600 + new Random().nextInt(300);
            redisTemplate.opsForValue().set(key, JSON.toJSONString(product), expireTime, TimeUnit.SECONDS);
            return product;
        } finally {
            redisTemplate.delete(lockKey); // 释放锁
        }
    }
}

🎯 3. 总结

问题现象解决方案
缓存穿透查询不存在的数据布隆过滤器、缓存空值
缓存击穿热点 key 过期后瞬时请求量大分布式锁、预加载
缓存雪崩大量 key 同时失效随机过期时间、限流

📢 4. 进阶提升方向

Redis Cluster 高可用架构实战
分布式锁的实现方式对比(Redis、ZooKeeper、Redisson)
Spring Cloud + Redis 限流与熔断策略

📌 如果你觉得文章有帮助,欢迎点赞👍 + 关注,后续会持续分享高并发架构实战! 🚀🚀🚀

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值