面试题:Redis如何防止缓存穿透 + 布隆过滤器原理

题目来源

招银网络-技术-1面

题目描述

  • 缓存穿透是什么?
  • 如何防止缓存穿透
  • 布隆过滤器的原理是什么?

我的回答

  • 缓存穿透是什么?
    攻击者大量请求缓存和数据库中都不存在的key。
  • 如何防止缓存穿透
    可以使用布隆过滤器
  • 布隆过滤器的原理是什么?
    记不太清楚了

更好的答案

  • 布隆过滤器的使用流程
    使用bitmap来存储合法的的商品id。
    当请求到达服务器之后,如果缓存中没有,则不直接访问数据库,而是先通过布隆过滤器进行筛选,如果通过了布隆过滤器的筛选,则可以去查询数据库了,查出来了之后,再放入缓存,并返回给客户端。
  • 布隆过滤器的原理
    在这个问题的场景下,我们是把布隆过滤器当做白名单来使用,只有布隆过滤器里面存在的商品id,才能通过过滤,然后去访问数据库。
    要实现时间复杂度为1的过滤,只能使用哈希算法,但是哈希算法经常会产生碰撞,就会产生误判。
    所以,布隆过滤器采用了多种哈希算法,当要查询一个商品id是否在白名单中时,布隆过滤器会计算hash_1(id), hash_2(id), hash_3(id)…hash_n(id),把所有计算出来的哈希值对bitmap的总位数取余,然后判断是否所有的取余之后的结果,都在bitmap中,如果都在,那该商品id通过过滤,如下所示,qqcr1为合法id,可以去请求数据库了。
    在这里插入图片描述
    布隆过滤器是如何初始化的呢?答案是将所有的合法的商品id经过布隆过滤器定义的哈希函数计算之后,对bitmap的位数取余,然后将该bit位设置为1,如下图所示
    在这里插入图片描述

如果有任何一个hash算法的结果取余之后不在bit毛中,则该商品id一定是攻击者伪造的,就不能去请求数据库,如下所以,qqcr2为非法id。
在这里插入图片描述

  • 布隆过滤器的优点
    使用bitmap来存储白名单,节省空间,并且计算哈希的时间复杂度是1,在判断商品是否存在时,可以使用如下方法:
    获取哈希函数值,对bitmap总位数取余,假如结果为3和1,然后利用位运算,将一个int类型或者long类型的字面量1左移两位和1位,就可以得到100,010,将100和010做位运算或运算,就可以得到110,然后将110与bitmap做位运算与操作,如果结果大于0,则通过布隆过滤器的过滤,就可以去数据库查询了。当然,上述过滤操作是我自己猜想的,真正的布隆过滤器,肯定不止有int的32位或者long的64位,具体的需要去查找怎么做的布隆过滤器。

  • 布隆过滤器的缺点
    – 缺点1:可能会发生误判的情况
    现在有两个合法商品id,id_1和id_2,一个不合法的商品id为id_3,有两个hash函数,计算出来的bitmap如下
    在这里插入图片描述
    id_3计算出来的bit位如下,可以看到id_3计算的bit位跟id_1和id_2的bit位产生了碰撞,则id_3可以通过布隆过滤器的过滤,产生误判。
    有没有什么办法可以解决这个误判的问题呢?
    如果只使用一个布隆过滤器来存储商品白名单,则不行,因为哈希碰撞肯定会存在。有另一个思路,我们可以通过增加bitmap的长度与增加哈希函数的个数,缓解哈希碰撞,从而缓解误判的概率。但是,需要注意的是,增加bitmap会占用更多的存储空间,增加哈希函数,需要进行更多次的计算,会占用更多的CPU时间片。
    在这里插入图片描述

    – 缺点2:删除数据困难,假如bitmap已经初始化好了,如果后台管理员删除了某几个商品,则布隆过滤器不能直接将该商品对应的bit位设置为0,因为其他商品计算出来的bit位可能跟被删除的商品一致。所以我们可以采取定期重新初始化布隆过滤器的策略,在某些商品被删除到重新初始化期间,可能会出现误判。

通过Redisson使用布隆过滤器

  • 添加maven依赖
<dependency>
	<groupId>org.redisson</groupId>
	<artifactId>redisson</artifactId>
	<version>3.16.1</version>
</dependency>
  • 配置redisson客户端
@Configuration
public class RedissonConfig {
	@Bean
	public RedissonClient redissonClient() {
		Config config = new Config();
		config.useSingleServer().setAddress("redis://localhost:6379");
		return Redisson.create(config);
	}
}
  • 初始化布隆过滤器
@PostConstruct
public void addProduct() {
	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);
}
  • 判断数据是否存在
public boolean mightcontain(Long id) {
	return bloomFilter.contains(id);
}

通过Guava使用布隆过滤器

Google Guava是 Google 开发和维护的开源 Java开发库,它包含许多基本的工具类,例如字符串处理、集合、并发工具、I/O和数学函数等等,当然,也包括布隆过滤器

  • 添加maven依赖
<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>31.0.1-jre<</version>
</dependency>
  • 创建布隆过滤器
BloomFilter<Integer> filter = BloomFilter.create(
	Funnels.integerFunnel(), // Funnel 是一个接口,用于将任意类型的对象转换为字节流,以便用于布隆过滤器的哈希计算。
	10000, // 插入数据条目数量
	0.001 // 误判率
);
  • 向布隆过滤器添加数据
@PostConstruct
public void addProduct() {
	logger.info("初始化布隆过滤器数据开始");
	//插入4个元素
	filter.put(1L);
	filter.put(2L);
	filter.put(3L);
	filter.put(4L);
	logger.info("初始化布隆过滤器数据结束");
}
  • 判断数据是否存在
    方法名就非常有 google 特色 , ”mightContain“ 的中文表意是:”可能存在“ 。方法的返回值为 true ,元素可能存在,但若返回值为 false ,元素必定不存在。
public boolean maycontain(Long id) {
	return filter.mightContain(id);
}

参考

bilibili视频-徐庶老师-面试官:说说布隆过滤器怎么解决缓存穿透的?

布隆过滤器:后端开发者必学的知识点!

小林coding:什么是缓存雪崩、击穿、穿透?

  • 29
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值