首页 > Spring Cloud
阅读:255
防止缓存穿透方案
缓存可以说是我们对数据库的一道保护墙,缓存穿透就是冲破了我们的保护墙,每个缓存都有一个缓存的 Key,当相同的 Key 过来时,我们就直接取缓存中的数据返回给调用方,而不用去查询数据库,如果调用方传来的永远都是我们缓存中不存在的 Key,这样每次都需要去数据库中查询一次,就会导致数据库压力增大,这样缓存就失去意义了,这就是所谓的缓存穿透。
1)如果查询数据库也为空的时候,把这个 key 缓存起来,这样在下次请求过来的时候就可以走缓存了。当然这种方案有个弊端,那就是请求过来的 key 必须大部分相同,如果受到攻击的话,每次的 key 肯定不是固定的,只要不固定 key,这个方案就没用。
2)可以用缓存 key 的规则来做一些限制,当然这种只适合特定的使用场景,比如我们查询商品信息,我们把商品信息存储在 Mongodb 中,Mongodb 有一个 _id 是自动生成的,它有一定的生成规则,如果是直接根据 id 查询商品,在查询之前我们可以对这个 id 做认证,看是不是符合规范,当不符合的时候就直接返回默认的值,既不用去缓存中查询,也不用操作数据库了。这种方案可以解决一部分问题,使用场景比较少。
3)利用布隆过滤器来实现对缓存 key 的检验,需要将所有可能缓存的数据 Hash 到一个足够大的 BitSet 中,在缓存之前先从布隆过滤器中判断这个 key 是否存在,然后做对应的操作。
布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率且删除困难。
布隆过滤器的使用场景比较多,比如我们现在讲的防止缓存穿透、垃圾邮件的检测等。Google chrome 浏览器使用 Bloom Filter 识别恶意链接,Goolge 在 BigTable 中也使用 Bloom Filter 以避免在硬盘中寻找不存在的条目。我公司使用布隆过滤器来对爬虫抓取的 Url 进行重复检查等。
下面我们通过一个小案例来看看 BloomFilter 怎么使用,代码如下所示。
那么调整之后的错误率有没有效果呢,我们重新执行下程序看看结果是否跟我们预想的一样,如果有效果的话,错误数量应该是 1010000×(0.0003/100)=3.03,可以允许错误数量在 3 到 4 个之间。
缓存穿透不能完全解决,我们只能将其控制在一个可以容忍的范围内,如果是用 Spring Cache 来缓存的话我们可能还用不了布隆过滤器,如果想要结合 Spring Cache 来使用的话我们必须对其扩展才行。
缓存穿透的危害
我们已经了解了什么是缓存穿透,其危害显而易见,当大量的请求过来时,首先会从缓存中去寻找数据,当缓存中没有对应的数据时又转到了数据库中去寻找,瞬时数据库的压力会很大,相当于没有用到缓存,同时还增加了去缓存中查找数据的时间。解决方案
缓存穿透有几种解决方案:1)如果查询数据库也为空的时候,把这个 key 缓存起来,这样在下次请求过来的时候就可以走缓存了。当然这种方案有个弊端,那就是请求过来的 key 必须大部分相同,如果受到攻击的话,每次的 key 肯定不是固定的,只要不固定 key,这个方案就没用。
2)可以用缓存 key 的规则来做一些限制,当然这种只适合特定的使用场景,比如我们查询商品信息,我们把商品信息存储在 Mongodb 中,Mongodb 有一个 _id 是自动生成的,它有一定的生成规则,如果是直接根据 id 查询商品,在查询之前我们可以对这个 id 做认证,看是不是符合规范,当不符合的时候就直接返回默认的值,既不用去缓存中查询,也不用操作数据库了。这种方案可以解决一部分问题,使用场景比较少。
3)利用布隆过滤器来实现对缓存 key 的检验,需要将所有可能缓存的数据 Hash 到一个足够大的 BitSet 中,在缓存之前先从布隆过滤器中判断这个 key 是否存在,然后做对应的操作。
布隆过滤器介绍
布隆过滤器(Bloom Filter)是 1970 年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率且删除困难。
布隆过滤器的使用场景比较多,比如我们现在讲的防止缓存穿透、垃圾邮件的检测等。Google chrome 浏览器使用 Bloom Filter 识别恶意链接,Goolge 在 BigTable 中也使用 Bloom Filter 以避免在硬盘中寻找不存在的条目。我公司使用布隆过滤器来对爬虫抓取的 Url 进行重复检查等。
代码示例
不得不说 Google Guava 真是一个万能的库,很多东西都封装好了,Bloom Filter 也有封装好的实现,我们直接使用即可。下面我们通过一个小案例来看看 BloomFilter 怎么使用,代码如下所示。
public static void main(String[] args) { // 总数量 int total = 1000000; BloomFilter<String> bf = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), total); // 初始化 10000 条数据到过滤器中 for (int i = 0; i < total; i++) { bf.put("" + i); } // 判断值是否存在过滤器中 int count = 0; for (int i = 0; i < total + 10000; i++) { if (bf.mightContain("" + i)) { count++; } } System.out.println("匹配数量 " + count); }通过 BloomFilter.create 创建一个布隆过滤器,初始化 1000000 条数据到过滤器中,然后在初始化数据的基础上加上 10000 条,分别去过滤器中检查是否存在,按照正常的逻辑来说,匹配的数量肯定是 1000000,事实上输出的结果如下:
匹配数量 1000309
大家肯定很好奇,明明多加的那 10000 条数据是不存在的,为什么匹配出的数量多出来 309 条?那是因为布隆过滤器是存在一定错误率的,我们可以调节布隆过滤器的错误率,在 create 的时候可以指定第 3 个参数来指定错误率,具体代码如下所示。BloomFilter<String> bf = BloomFilter.create(Funnels.stringFunnel(Charsets. UTF_8), total, 0.0003);
错误率调节是一个 double 类型的参数,默认值是 0.03,值越小错误率越小,同时存储空间会越大,这个可以根据需求去调整。那么错误率是怎么计算的呢,我们总共是 1010000 条数据去测试是否匹配,默认值是 0.03,那么错误率就是 1010000×(0.03/100)=303,刚刚测试的错误数量是 309,可见处于这个范围内。那么调整之后的错误率有没有效果呢,我们重新执行下程序看看结果是否跟我们预想的一样,如果有效果的话,错误数量应该是 1010000×(0.0003/100)=3.03,可以允许错误数量在 3 到 4 个之间。
匹配数量 1000004
利用布隆过滤器我们可以预先将缓存的数据存储到过滤器中,比如用户 ID。当根据 ID 来查询数据的时候,我们先从过滤器中判断是否存在,存在的话就继续下面的流程,不存在直接返回空即可,因为我们认为这是一个非法的请求。缓存穿透不能完全解决,我们只能将其控制在一个可以容忍的范围内,如果是用 Spring Cache 来缓存的话我们可能还用不了布隆过滤器,如果想要结合 Spring Cache 来使用的话我们必须对其扩展才行。
所有教程
- socket
- Python基础教程
- C#教程
- MySQL函数
- MySQL
- C语言入门
- C语言专题
- C语言编译器
- C语言编程实例
- GCC编译器
- 数据结构
- C语言项目案例
- C++教程
- OpenCV
- Qt教程
- Unity 3D教程
- UE4
- STL
- Redis
- Android教程
- JavaScript
- PHP
- Mybatis
- Spring Cloud
- Maven
- vi命令
- Spring Boot
- Spring MVC
- Hibernate
- Linux
- Linux命令
- Shell脚本
- Java教程
- 设计模式
- Spring
- Servlet
- Struts2
- Java Swing
- JSP教程
- CSS教程
- TensorFlow
- 区块链
- Go语言教程
- Docker
- 编程笔记
- 资源下载
- 关于我们
- 汇编语言
- 大数据
- 云计算
- VIP视频