什么是缓存穿透以及常见解决方案
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库,导致数据库压力提高,造成宕机。
缓存穿透就是指用户访问那些在数据库和Redis中都不存在的数据,例如我们知道id采用自增策略,那么就不可能出现负数id,而如果不法分子使用负数id进行查询,那么这些请求都会穿过Redis直接向数据库发送请求,从而导致数据库压力骤增,导致数据库宕机。
常见解决方案:
缓存空对象
优点:实现简单,维护方便;缺点:额外的内存消耗,可能造成短期的不一致
布隆过滤
优点:内存占用较少,没有多余key;缺点:存在误判可能(有穿透的风险),无法删除数据
(注 上面两种方式都是被动的解决缓存穿透方案,此外我们还可以采用主动的方案预防缓存穿透,比如:增强id的复杂度避免被猜测id规律、做好数据的基础格式校验、加强用户权限校验、做好热点参数的限流)
布隆过滤器
介绍:
布隆过滤器可以用于检索一个元素是否在一个集合中。
它的优点是空间效率和查询时间都远超一般的算法,缺点是有一定的误识别率和删除困难,原因是因为对于不同的数据,可能会出现使用同一个hash函数得出相同的值,从而出现误判。
原理:
当一个元素被加入集合时,通过 K 个散列函数将这个元素映射成一个位数组中的 K 个点,把它们置为 1。检索时,我们只要看看这些点是不是都是 1 就(大约)知道集合中有没有它了:如果这些点有任何一个 0,则被检元素一定不在;如果都是 1,则被检元素很可能在。
实现:
(对于布隆过滤器的准确度,其影响因素包含Hash函数的随机性,二进制向量的大小)
这里使用Redission实现:
1、添加Maven依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.1</version>
</dependency>
2、配置 Redisson 客户端
@Configuration
public class RedissonConfig {
Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379");
return Redisson.create(config);
}
}
3、初始化
RBloomFilter<Long> bloomFilter = redissonClient.
getBloomFilter("myBloomFilter");
//10000表示插入元素的个数,0.001表示误判率
bloomFilter.tryInit(10000, 0.001);
//插入4个元素
bloomFilter.add(1L);
bloomFilter.add(2L);
bloomFilter.add(3L);
bloomFilter.add(4L);
4、判断数据是否存在
public boolean mightcontain(Long id) {
return bloomFilter.contains(id);
}
对于不支持删除的问题:
可以编写一个定时任务,在一定事件之后,创建一个新的布隆过滤器,然后把数据库中的全量数据查询出来然后使用映射将其映射到新的布隆过滤器中,之后更新原有的布隆过滤器的指针。