缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。以下是几种解决缓存穿透的方法:
布隆过滤器是一种空间效率极高的概率型数据结构,用于判断一个元素是否在一个集合中。它可以告诉你一个元素“可能在集合中”或“肯定不在集合中”。
当查询数据库返回空结果时,也将这个空结果缓存起来,并设置一个较短的过期时间。
public Object get(String key) {
Object value = cache.get(key);
if (value == null) {
value = db.query(key);
if (value == null) {
cache.put(key, new NullObject());
// 设置一个较短的过期时间
cache.expire(key, 60);
} else {
cache.put(key, value);
}
}
return value;
}
在缓存失效的时候,不是立即去加载数据库,而是先使用互斥锁,保证只有一个线程去加载数据,其他线程等待结果。
public Object get(String key) {
Object value = cache.get(key);
if (value == null) {
String lockKey = "lock:" + key;
if (cache.tryLock(lockKey)) {
try {
value = cache.get(key); // 再次检查缓存
if (value == null) {
value = db.query(key);
cache.put(key, value);
}
} finally {
cache.unlock(lockKey);
}
} else {
// 等待一段时间后重试
Thread.sleep(100);
return get(key);
}
}
return value;
}
结合布隆过滤器和缓存空对象的方法,可以进一步减少无效查询。
public Object get(String key) {
if (bloomFilter.mightContain(key)) {
Object value = cache.get(key);
if (value == null) {
value = db.query(key);
if (value == null) {
cache.put(key, new NullObject());
cache.expire(key, 60);
} else {
cache.put(key, value);
}
}
return value;
} else {
return null; // 直接返回null,避免无效查询
}
}
使用两级缓存架构,第一级缓存是本地缓存(如Caffeine),第二级缓存是分布式缓存(如Redis)。当本地缓存失效时,再去分布式缓存查询,如果分布式缓存也不存在,则查询数据库并更新两级缓存。
public Object get(String key) {
Object value = localCache.getIfPresent(key);
if (value == null) {
value = distributedCache.getIfPresent(key);
if (value == null) {
value = db.query(key);
if (value == null) {
localCache.put(key, new NullObject());
distributedCache.put(key, new NullObject());
distributedCache.expire(key, 60);
} else {
localCache.put(key, value);
distributedCache.put(key, value);
}
} else {
localCache.put(key, value);
}
}
return value;
}
选择哪种方法取决于具体的应用场景和需求。布隆过滤器和缓存空对象是比较常用的方法,可以有效地解决缓存穿透问题。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。