缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。以下是几种解决缓存穿透的方法:
布隆过滤器是一种空间效率极高的概率型数据结构,用于判断一个元素是否在一个集合中。它可以告诉你一个元素“可能在集合中”或“肯定不在集合中”。
当查询数据库发现数据不存在时,也将这个空结果缓存起来,并设置一个较短的过期时间。
// 示例代码 String key = "nonexistent_key"; if (cache.get(key) == null) { Object value = database.query(key); if (value == null) { cache.put(key, "NULL", shortExpirationTime); } else { cache.put(key, value, longExpirationTime); } } 在缓存失效的时候,不是立即去加载数据库,而是先使用互斥锁,保证只有一个线程去加载数据,其他线程等待结果。
// 示例代码 String key = "key_to_query"; if (cache.get(key) == null) { synchronized (this) { if (cache.get(key) == null) { Object value = database.query(key); cache.put(key, value, expirationTime); } } } 使用布隆过滤器来过滤掉大部分不存在的键,对于布隆过滤器判断为可能存在的键,再通过缓存空对象的方式处理。
在应用层增加一级本地缓存,比如使用Guava Cache或者Caffeine,这样即使分布式缓存穿透,本地缓存也能提供一定的保护。
在系统启动时,预先将热点数据加载到缓存中,减少冷启动时的缓存穿透问题。
在高并发场景下,可以临时关闭某些非核心功能的缓存,保证核心功能的稳定运行。
选择哪种方法取决于具体的业务场景和需求。通常情况下,结合多种方法使用效果会更好。