无论在开发过程中还是面试过程中,这三个问题总是被遇到。下面是各个问题的原因和解决方案。
缓存穿透
原因
- 缓存穿透其实是缓存的单点问题,是指查询一个一定不存在的数据。如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到 DB 去查询,可能导致 DB 挂掉。这种情况大概率是遭到了攻击。
- 同时也可能是由于恶意请求或非常罕见的请求导致的,原理同上。如果这种情况频繁发生,会导致大量的请求直接访问底层数据源,增加了系统的负载并降低了性能。
解决方案
缓存空值
public class Cache {
private Map<String, String> cacheMap; //这里也可以使用RedisTemplate
public Cache() {
cacheMap = new HashMap<>();
}
public String getFromCache(String key) {
// 先从缓存中查找数据
String value = cacheMap.get(key);
// 如果缓存中不存在该数据
if (value == null) {
// 查询底层数据源
value = queryDataFromDataSource(key);
// 如果底层数据源中不存在该数据
if (value == null) {
// 将空值缓存起来,防止缓存穿透
cacheMap.put(key, "");
} else {
// 将数据放入缓存
cacheMap.put(key, value);
}
}
return value;
}
private String queryDataFromDataSource(String key) {
// 查询底层数据源的逻辑
// ...
return null; // 假设底层数据源中不存在该数据
}
}
将空值给缓存起来,这样就减少了数据库的交互。
布隆过滤器
布隆过滤器是一种空间效率高、查询时间快的概率型数据结构,用于判断一个元素是否存在于一个集合中。它可以帮助我们快速判断一个请求是否是恶意请求,从而减轻底层数据源的负载。
以下是使用布隆过滤器来解决缓存穿透问题的具体步骤:
-
初始化布隆过滤器:根据预估的数据规模和期望的误判率,初始化一个布隆过滤器。布隆过滤器的大小和误判率是成正比的,需要根据实际情况来选择合适的参数。
-
添加元素到布隆过滤器:将所有可能存在于缓存中的关键字都添加到布隆过滤器中。每个关键字经过多次哈希函数计算后,会在布隆过滤器中的对应位上被标记为1。
-
查询请求是否存在于布隆过滤器中:当一个请求到来时,先用相同的哈希函数计算出请求的关键字。然后检查布隆过滤器中对应的位是否都为1。如果所有位都为1,说明请求可能存在于缓存中;如果有任何一位为0,说明请求一定不存在于缓存中。
-
进一步验证请求:如果布隆过滤器判断请求可能存在于缓存中,那么可以进一步查询缓存来验证请求的确切结果。如果布隆过滤器判断请求一定不存在于缓存中,可以直接返回缓存不存在的结果,避免查询底层数据源。
import com.google.common.hash.BloomFilter; import com.google.common.hash.Funnels; public class BloomFilterExample { public static void main(String[] args) { // 创建一个布隆过滤器,预期插入的元素数量为10000,期望的误判率为0.1% BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.unencodedCharsFunnel(), 10000, 0.001); // 向布隆过滤器中添加元素 bloomFilter.put("exampleKey1"); bloomFilter.put("exampleKey2"); // 检查元素是否存在于布隆过滤器中 boolean exists1 = bloomFilter.mightContain("exampleKey1"); boolean exists2 = bloomFilter.mightContain("exampleKey3"); System.out.println("Exists 1: " + exists1); // 输出: Exists 1: true System.out.println("Exists 2: " + exists2); // 输出: Exists 2: false } }
注意:
标签:缓存,请求,过期,Redis,jedis,雪崩,过滤器,String From: https://www.cnblogs.com/pronting/p/17714201.html