# Redis缓存穿透、缓存击穿、缓存雪崩、热点Key的示例分析 ## 一、缓存穿透(Cache Penetration) ### 1.1 概念与场景 缓存穿透指**查询不存在的数据**,导致请求直接穿透缓存层到达数据库。常见场景: - 恶意攻击:故意请求不存在的ID(如负值、超大数值) - 业务误操作:前端传递无效参数(如已删除的商品ID) ### 1.2 示例代码 ```java // 错误示例:未做空值校验 public Product getProduct(Long id) { // 1. 先查缓存 Product product = redisTemplate.opsForValue().get("product:" + id); if (product == null) { // 2. 缓存未命中,直接查库 product = productMapper.selectById(id); // 可能返回null redisTemplate.opsForValue().set("product:" + id, product); } return product; }
// 改进代码 if (product == null) { product = productMapper.selectById(id); if (product == null) { redisTemplate.opsForValue().set("product:" + id, "NULL", 5, TimeUnit.MINUTES); return null; } redisTemplate.opsForValue().set("product:" + id, product); }
# Python示例 from pybloom_live import ScalableBloomFilter bf = ScalableBloomFilter(initial_capacity=1000) # 预热阶段加载所有有效ID for id in valid_ids: bf.add(id) # 查询前校验 if not id in bf: return None
某个热点Key突然过期时,大量并发请求直接击穿到数据库。典型场景: - 秒杀商品缓存过期 - 首页热门资讯缓存失效
public Product getProductWithLock(Long id) { String lockKey = "lock:product:" + id; try { // 尝试获取分布式锁 Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS); if (locked) { Product product = productMapper.selectById(id); redisTemplate.opsForValue().set("product:" + id, product, 1, TimeUnit.HOURS); return product; } else { Thread.sleep(100); // 短暂等待后重试 return getProductWithLock(id); } } finally { redisTemplate.delete(lockKey); } }
// 缓存数据结构 { "value": "真实数据", "expire_time": 1672502400 // 逻辑过期时间戳 }
大量Key同时过期或Redis集群宕机,导致请求全部涌向数据库。典型案例: - 凌晨批量任务刷新所有缓存 - Redis主从切换期间
// 设置基础过期时间+随机偏移量 int baseExpire = 3600; int randomExpire = new Random().nextInt(300); redisTemplate.opsForValue().set(key, value, baseExpire + randomExpire, TimeUnit.SECONDS);
请求 → CDN → 本地缓存(Caffeine) → Redis → DB
# 伪代码示例 def get_data(key): if circuit_breaker.is_open(): return get_data_from_backup() try: data = redis.get(key) if not data: data = db.query(key) redis.setex(key, ttl, data) return data except Exception as e: circuit_breaker.record_failure() raise e
// Guava本地缓存 LoadingCache<String, Product> localCache = CacheBuilder.newBuilder() .maximumSize(1000) .expireAfterWrite(10, TimeUnit.SECONDS) .build(new CacheLoader<String, Product>() { @Override public Product load(String key) { return getFromRedis(key); // 从Redis集群读取 } });
# 将hot_key拆分为hot_key_1, hot_key_2... shard_id = hash(user_id) % 4 real_key = f"hot_key_{shard_id}" redis.get(real_key)
问题类型 | 核心特征 | 典型解决方案 |
---|---|---|
缓存穿透 | 查询不存在数据 | 布隆过滤器、空值缓存 |
缓存击穿 | 单热点Key失效 | 互斥锁、逻辑过期 |
缓存雪崩 | 大量Key同时失效 | 差异化TTL、多级缓存 |
热点Key | 单Key访问量极高 | 本地缓存、Key分片 |
实际生产环境中,建议结合监控系统(如Prometheus)和动态配置中心实现策略的实时调整。 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。