在Java项目中使用布隆过滤器解决缓存穿透问题

 前言

在上文中详细提到了在Linux中使用Docker安装Redis容器以及手动安装布隆过滤器插件

文章地址:

Linux中安装Redis布隆过滤器插件

本文将整合项目来介绍如何使用布隆过滤器插件。

概念

        在这里先简单介绍一下缓存穿透的概念:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源(DB服务器)。

        即查询一个不存在的数据,数据库mysql中查询不到也不会写入缓存,就导致每次请求都查到数据库。 比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,此漏洞可能压垮数据库。

        至于布隆过滤器的原理,大家可以去搜索一下,有许多写的很好的文章,此处就不多做赘述。

解决方式:
方式一:

缓存空数据,当查询数据返回为空时,任然给这个空结果缓存,即赋null值。并设置一个比较短的过期时间。(不超过5分钟)。

优点:简单。

缺点:消耗内存,可能会发生不一致。

方案二:

使用布隆过滤器。

其工作方式就是将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

JAVA项目整合        

官方文档地址:GitHub - RedisBloom/JRedisBloom: Java Client for RedisBloom probabilistic module

 导入依赖:

       <dependency>
            <groupId>com.redislabs</groupId>
            <artifactId>jrebloom</artifactId>
            <version>2.2.2</version>
        </dependency>

基本api使用demo:

/**JreBloom提供的jedis可以操控redis中的布隆过滤器********************************/
        @Test
      public void testJedis(){
        //经过测试此是操作redis中的布隆过滤器
          //改为jedisPool连接池
          //Client支持各种方式
          // Jedis jedis= JedisUtil.getJedis();
          // Client client=new Client(jedis);
          Client client=new Client("127.0.0.1",6379);
          

          //测试是否和redis容器同步
          System.out.println(client.exists("phoneid", "9999"));
          System.out.println(client.exists("phoneid", "6666"));

          //单个添加操作
          client.add("phoneid","0716");
          client.exists("phoneid","0716");

          //批量添加
          client.addMulti("phoneid","name1","name2","name3");
          boolean[] booleans = client.existsMulti("phoneid", "name1", "name2", "name3", "name4");
            for (int i = 0; i < booleans.length; i++) {
                System.out.println("第"+i+booleans[i]+"个");
            }

          //创建一个过滤器            key           容量             容错率

          client.createFilter("userid",100000,0.01);  //如若重复创建会报错
          //需要进行异常处理


          client.bfInsert("userid","999");
          //两者都是往布隆过滤器中插值
          System.out.println(client.add("userid", "999"));//add方法如果元素存在则返回false   应该返回false
          boolean[] userids = client.bfInsert("userid", "999");//如果元素存在则覆盖值
            for (boolean userid : userids) {
                System.out.println(userid);
           }

         // client.delete(key)  删除过滤器
        }

小插曲:

起初以为其提供的Client中使用的Jedis没有使用JedisPool连接池,后经过自己配置了一个Jedis连接池后,测试单例模式时发现其Client对象中有一个JedisPool。

后经过源码查询发现,Client支持的几种方式。其有自带的JedisPool。

官方文档演示默认演示是这种方式。

整合业务

示例图:

本项目以电商系统为例,业务实现案例:

 //详情业务    加入Redis  TODO 加入布隆过滤器  防止缓存穿透
    @Override
    public List<Phone> findPhoneByPid(Integer pid) {
        List<Phone> list=null;
        Client client=JedisClientSingleton.getInstance(); //利用双重检查所确保client对象单例
        //防止缓存穿透 先从           布隆过滤器中查询--->redis---->DB     未查询到则直接返回
        if(client.exists("phoneid", String.valueOf(pid))){  //布隆过滤器判断 如果不存在 则一定不存在于缓存和DB中 直接返回
            if (this.cacheService.hasKey(String.valueOf(pid))) {    //hasKey(key) 判断是否存在该key
                list=this.cacheService.getList(pid,Phone.class);   //如果存在该Key 则从Redis中拿取
            }else {
                list=comDao.selectPhoneByPid(pid);   //否则走DB 从数据库中拿取
                this.cacheService.add(pid,list,EXPIRE_TIME, EXPIRE_TIME_TYPE);     // 然后将该List数据存入Redis 并设置过期时间  TODO 过期时间应该随机设置--防止缓存雪崩
                //那么下次则会走Redis 而不会走DB
            }
            return list;
        }
        return null; //不存在直接返回空
    }

BUG记录:

在创建好一个布隆过滤器后,将虚拟机重启一次后发现布隆过滤器不存在,后通过命令查看redis数据持久化是否开启CONFIG GET appendonly结果返回no,但配置文件中已经修改为yes,后检查,发现是当时docker命令启动redis的命令有问题,进入容器中检查配置文件发现目录中没有挂载的配置文件,后发现启动命令目录弄错了,于是修改为正确的目录和文件。后解决此问题

  • 13
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Sure!以下是一个示例代码,展示了如何在Java Spring Boot使用布隆过滤器和Redis来解决缓存穿透问题: 首先,你需要在pom.xml文件添加相应的依赖: ```xml <dependencies> <!-- Spring Boot Starter Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- Guava Bloom Filter --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1-jre</version> </dependency> </dependencies> ``` 接下来,创建一个布隆过滤器的工具类 BloomFilterUtil.java: ```java import com.google.common.hash.BloomFilter; import com.google.common.hash.Funnels; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; @Component public class BloomFilterUtil { @Autowired private RedisTemplate<String, Object> redisTemplate; private BloomFilter<String> bloomFilter; // 设置布隆过滤器的预计插入数据量和误判率 private static final int EXPECTED_INSERTIONS = 1000000; private static final double FPP = 0.001; @PostConstruct public void init() { // 创建布隆过滤器,并将其保存到Redis bloomFilter = BloomFilter.create(Funnels.stringFunnel(), EXPECTED_INSERTIONS, FPP); redisTemplate.opsForValue().set("bloomFilter", bloomFilter); } public boolean mightContain(String key) { // 从Redis获取布隆过滤器 bloomFilter = (BloomFilter<String>) redisTemplate.opsForValue().get("bloomFilter"); // 使用布隆过滤器判断key是否可能存在 return bloomFilter.mightContain(key); } public void put(String key) { // 从Redis获取布隆过滤器 bloomFilter = (BloomFilter<String>) redisTemplate.opsForValue().get("bloomFilter"); // 将key添加到布隆过滤器 bloomFilter.put(key); // 将更新后的布隆过滤器保存到Redis redisTemplate.opsForValue().set("bloomFilter", bloomFilter); } } ``` 然后,在你需要使用布隆过滤器解决缓存穿透的地方,注入 BloomFilterUtil,并使用它来判断数据是否存在于缓存: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class CacheController { @Autowired private BloomFilterUtil bloomFilterUtil; @GetMapping("/data/{key}") public String getData(@PathVariable String key) { // 先使用布隆过滤器判断key是否可能存在于缓存 if (bloomFilterUtil.mightContain(key)) { // 如果可能存在,再从缓存获取数据 String data = redisTemplate.opsForValue().get(key); if (data != null) { return data; } } // 如果数据不在缓存,进行其他操作(例如从数据库查询数据) // ... return null; } } ``` 这样,当有大量的请求同时访问某个缓存时,在经过布隆过滤器的判断后,可以避免无效的缓存查询请求,减轻了数据库的负载压力。 请注意,以上代码只是示例,实际使用时需要根据具体的业务需求进行适当的修改和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值