缓存穿透场景描述:一般判断用户是否在缓存中,如果存在则直接返回结果,不存在则查询数据库,如果来一波冷数据,会导致缓存大量击穿,造成数据库宕机。
常用解决方法:
- 缓存空值:将第一次查询的为空的数据也缓存至redis中,并且设置一个过期时间防止redis内存占满
- 布隆过滤器:布隆过滤器当缓存的索引,只有在布隆过滤器中,才去查询缓存,如果没查询到则穿透到数据库查询。如果不在布隆过滤器中,则直接返回,但是会造成一定程度的误判
什么是布隆过滤器
布隆过滤器(Bloom Filter),是一种非常节省空间的概率数据结构,运行速度快,占用内存小,但是有一定的误判率且无法删除元素。它实际上是一个很长的二进制向量和一系列随机映射函数组成,主要用于判断一个元素是否在一个集合中。
布隆过滤器的优点:
- 支持海量数据场景下高效判断元素是否存在
- 布隆过滤器存储空间小,并且节省空间,不存储数据本身,仅存储hash结果取模运算后的位标记
- 不存储数据本身,比较适合某些保密场景
布隆过滤器的缺点:
- 不存储数据本身,所以只能添加但不可删除,因为删掉元素会导致误判率增加
- 由于存在hash碰撞,匹配结果如果是“存在于过滤器中”,实际不一定存在
- 当容量快满时,hash碰撞的概率变大,插入、查询的错误率也就随之增加了
Java集成Redis使用布隆过滤器
pom中引入redisson依赖:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.1</version>
</dependency>
编写代码测试
public void patchingConsum(ConsumPatchingVO vo) throws ParseException {
Config config = new Config();
SingleServerConfig singleServerConfig = config.useSingleServer();
singleServerConfig.setAddress("redis://127.0.0.1:6379");
singleServerConfig.setPassword("123456");
RedissonClient redissonClient = Redisson.create(config);
RBloomFilter<String> bloom = redissonClient.getBloomFilter("name");
// 初始化布隆过滤器; 大小:100000,误判率:0.01
bloom.tryInit(100000L, 0.01);
// 新增10万条数据
for(int i=0;i<100000;i++) {
bloom.add("name" + i);
}
// 判断不存在于布隆过滤器中的元素
List<String> notExistList = new ArrayList<>();
for(int i=0;i<100000;i++) {
String str = "name" + i;
boolean notExist = bloom.contains(str);
if (notExist) {
notExistList.add(str);
}
}
if (StringUtils.isNotEmpty(notExistList) && notExistList.size() > 0 ) {
System.out.println("误判次数:"+notExistList.size());
}
}