缓存穿透

缓存穿透,指的是查询时,缓存和数据库中都不存在该数据,这样的查询每次都会查询到数据库,会导致数据库的压力过大。

解决方式通常有两种:

  1. 对数据库中不存在的值也进行缓存,比如对空值在Redis中缓存一个特殊的字符串,比如&(*&(#Q&$&#。
  2. 布隆过滤器

布隆过滤器

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

原理

布隆过滤器使用位数组来记录元素是否存在于集合当中。
当一个元素被加入集合时,会使用k个哈希函数分别对其进行计算得到k个哈希值。接着用k个哈希值分别对位数组的长度进行取余,得到k个数组下标,此时会将这k个下标下的值都置为1。
当需要判断一个元素是否在集合中时,使用k个哈希函数分别对其进行计算得到k个哈希值。接着用k个哈希值分别对位数组的长度进行取余,得到k个数组下标,判断这k个下标上的值是否都为1,如果有一个不为1,那么肯定不存在,如果都为1,则存在。

不同的元素可能得到相同的哈希值,所以布隆过滤器存在一定概率的误判,即布隆过滤器说某个元素存在,有概率会误判

解决缓存穿透

在项目启动后,可以将数据库中的数据添加到布隆过滤器中,当查询缓存不存在时,通过布隆过滤器判断数据库中是否存在,如果不存在就不进行DB查询了,减少数据库的压力。

实现与使用

1. guava实现

  1. <dependency>
  2. <groupId>com.google.guava</groupId>
  3. <artifactId>guava</artifactId>
  4. <version>30.1-jre</version>
  5. </dependency>
  6. public class BloomFilterUse {
  7. private static BloomFilter<Integer> bf = BloomFilter.create(Funnels.integerFunnel(), 1000000);
  8. public static void main(String[] args) {
  9. for (int i = 0; i < 1000000; i++) {
  10. bf.put(i);
  11. }
  12. int count = 0;
  13. for (int i = 1000000; i < 1100000; i++) {
  14. if (bf.mightContain(i)) {
  15. count++;
  16. }
  17. }
  18. System.out.println(count + "个判断错误");//3033个 约0.03的概率
  19. }
  20. }

2. redisson实现

  1. public class RedissonBloomFilterUse {
  2. public static void main(String[] args) {
  3. Config config = new Config();
  4. config.useSingleServer().setAddress("redis://127.0.0.1:6379");
  5. RedissonClient redissonClient = Redisson.create(config);
  6. RBloomFilter<String> goods = redissonClient.getBloomFilter("goods");
  7. goods.tryInit(100000L,0.03);
  8. goods.add("goods_123123");
  9. System.out.println(goods.contains("goods_123123"));
  10. }
  11. }