缓存穿透、缓存雪崩和缓存击穿是分布式系统中常见的缓存问题,它们都会导致缓存失效,进而对数据库或其他底层存储系统造成压力。以下是它们的详细说明及解决方案:
1. 缓存穿透(Cache Penetration)
定义
缓存穿透是指查询一个不存在的数据,由于缓存中没有该数据,请求会直接落到数据库上。如果大量请求同时查询不存在的数据,数据库可能会被压垮。
原因
- 恶意攻击:攻击者故意查询不存在的数据。
- 业务逻辑问题:程序逻辑错误,导致频繁查询不存在的数据。
解决方案
-
缓存空值:
- 如果查询结果为空,仍然将空值缓存起来,并设置一个较短的过期时间。
- 示例:
if (data == null) { cache.put(key, NULL_VALUE, 60); // 缓存空值,过期时间 60 秒 }
-
布隆过滤器(Bloom Filter):
- 使用布隆过滤器判断数据是否存在。如果布隆过滤器判断数据不存在,则直接返回,避免查询数据库。
- 示例:
if (!bloomFilter.mightContain(key)) { return null; // 数据不存在,直接返回 }
-
接口层校验:
- 在接口层对请求参数进行校验,过滤掉非法请求。
2. 缓存雪崩(Cache Avalanche)
定义
缓存雪崩是指大量缓存同时失效,导致所有请求都落到数据库上,数据库压力骤增,甚至崩溃。
原因
- 缓存设置了相同的过期时间。
- 缓存服务器宕机。
解决方案
-
设置不同的过期时间:
- 在设置缓存过期时间时,增加一个随机值,避免大量缓存同时失效。
- 示例:
int expireTime = 3600 + new Random().nextInt(600); // 过期时间在 3600~4200 秒之间 cache.put(key, value, expireTime);
-
缓存高可用:
- 使用 Redis 集群或其他高可用方案,避免单点故障。
-
限流和降级:
- 使用限流工具(如 Sentinel、Hystrix)限制数据库的请求量。
- 在缓存失效时,返回默认值或错误提示,避免直接访问数据库。
-
缓存预热:
- 在系统启动时,提前加载热点数据到缓存中。
3. 缓存击穿(Cache Breakdown)
定义
缓存击穿是指某个热点数据过期,同时有大量请求访问该数据,导致请求直接落到数据库上。
原因
- 热点数据过期。
- 大量请求同时访问该热点数据。
解决方案
-
互斥锁(Mutex Lock):
- 在缓存失效时,使用分布式锁(如 Redis 的
SETNX
)保证只有一个线程去查询数据库,其他线程等待。 - 示例:
String value = cache.get(key); if (value == null) { if (lock.tryLock()) { // 获取分布式锁 try { value = db.get(key); // 查询数据库 cache.put(key, value); // 更新缓存 } finally { lock.unlock(); // 释放锁 } } else { Thread.sleep(100); // 等待一段时间后重试 return getData(key); // 重试 } }
- 在缓存失效时,使用分布式锁(如 Redis 的
-
永不过期:
- 对热点数据设置永不过期,通过后台任务定期更新缓存。
- 示例:
cache.put(key, value); // 永不过期
-
逻辑过期:
- 在缓存中存储数据的过期时间,而不是依赖缓存的过期机制。当数据过期时,异步更新缓存。
- 示例:
if (cache.getExpireTime(key) < System.currentTimeMillis()) { // 异步更新缓存 executor.submit(() -> { String newValue = db.get(key); cache.put(key, newValue); }); }
4. 总结
问题 | 定义 | 原因 | 解决方案 |
---|---|---|---|
缓存穿透 | 查询不存在的数据,请求落到数据库 | 恶意攻击或业务逻辑问题 | 缓存空值、布隆过滤器、接口层校验 |
缓存雪崩 | 大量缓存同时失效,请求落到数据库 | 缓存设置相同过期时间或缓存服务器宕机 | 设置不同过期时间、缓存高可用、限流降级 |
缓存击穿 | 热点数据过期,大量请求落到数据库 | 热点数据过期 | 互斥锁、永不过期、逻辑过期 |
5. 面试回答建议
在面试中回答这个问题时,可以按照以下思路:
- 分别解释缓存穿透、缓存雪崩和缓存击穿的定义和原因。
- 结合实际项目经验,举例说明如何解决这些问题。
- 强调解决方案的适用场景(如布隆过滤器适合缓存穿透,互斥锁适合缓存击穿)。
这样回答既展示了你的技术深度,也体现了你对缓存问题的理解和解决能力。