上海古都建筑设计集团,上海办公室装修设计公司,上海装修公司高质量的内容分享社区,上海装修公司我们不是内容生产者,我们只是上海办公室装修设计公司内容的搬运工平台

Redis 布隆过滤器

guduadmin513月前

布隆过滤器

这一篇文章主要是记录布隆过滤器的使用和认识

主要参考了如下的blog

https://blog.csdn.net/weixin_42972832/article/details/131211665

他讲的还不错

简单的来说,布隆过滤器,实际上就像是一个集合,拿redis的key来举例来说,布隆过滤器的设置就是去过滤不属于redis key集合的key,这个方法还算挺有效的

原理初探

我理解到,布隆过滤器,底层就是利用hash函数

首先布隆过滤器一般是bitmap

传来一个key,通过几个hash函数,生成几个index的位置,

然后一个一个去查这几个index位置上的bitmap,是否都是1,如果都是1,那么就说明这个key存在于这个集合中,那我们就要放行

这里的算法其实应该是多种多样,但是万变不离其中,就是使用hash匹配

Redis 布隆过滤器,在这里插入图片描述,第1张

其实很好理解拉,不能懂!

问题

  • 误判的问题

    这里学过hash函数的很容易想到,这里可能会发生hash碰撞,如果一个key,他刚好等于已经存在的key的hash的化,就会发生hash碰撞,这就是会发生误判的理由

    但是可以知道的是,如果说,过滤之后不在集合里边,那么就说名集合里边一定没有这个key,这个原理大家基本都懂,hash一般是不可逆的,

    布隆过滤器: 不存在一定不存在,存在有可能存在,有可能不存在,有误判的可能

    • 不能删除的问题

      因为布隆过滤器底层是多个hash共享数组的位置的,所以如果说,我们要删除某个key的化,就会影响到别人,所以布隆过滤器就是不能删除,只能重构

      由于重构引出的问题就是,有可能重构的成本太大了,你有1亿条数据要重构,这成本太高了

      手动实现

      我这里的手动实现也是参考他的博客来看的,算是最简单的

      先来看工具类

      import com.hmdp.filter.BloomFilterInit;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.data.redis.core.RedisTemplate;
      import org.springframework.stereotype.Component;
      @Slf4j
      @Component
      public class CheckUtils {
          @Autowired
          private RedisTemplate redisTemplate;
          /**
           * 布隆过滤器校验
           *
           * @param key
           * @return boolean
           * @author hc
           * @date 2023/6/15 11:42
           */
          public boolean checkData(String key) {
              int abs = Math.abs(key.hashCode());
              long index = (long) (abs % Math.pow(2, 32));
              return redisTemplate.opsForValue().getBit(BloomFilterInit.WHITELIST_USER_KRY, index);
          }
          /**
           * 获取偏移量
           * @param key
           * @return long
           * @author hc
           * @date 2023/6/15 17:19
           */
          public long getOffsetId(String key) {
              int abs = Math.abs(key.hashCode());
              return getIndex(abs);
          }
          /**
           * 计算偏移量
           *
           * @param abs
           * @return java.lang.Long
           * @author hc
           * @date 2023/6/15 16:25
           */
          public long getIndex(int abs) {
              if (0 == abs) {
                  return 0L;
              }
              return (long) (abs % Math.pow(2, 32));
          }
      }
      

      因为这里使用最简单的方法,所以直接就用java的hashCode方法得到hash值,然后这里的bitmap 我的容量大小是2的32次方

      看这个工具类,也很好理解

      生成index,就是hash值 % 2 ^32

      就是这里的checkData比较特殊一点,先是获得index的位置,然后去redis中的bitmap中查找,如果有返回true,没有返回false

      controller 测试类

      @RestController
      @RequestMapping("/bloom")
      public class BloomFilterController {
          @Autowired
          private BloomFilterService bloomFilterService;
          @GetMapping("/add")
          public void addUser(String phone) {
              bloomFilterService.addUser(phone);
          }
          @GetMapping("/query/{id}")
          public void queryUser(@PathVariable Long id) {
              bloomFilterService.queryUser(id);
          }
      }
      

      一个添加用户

      一个查用户

      public interface BloomFilterService {
          void addUser(String phone);
          User queryUser(Long id);
      }
      

      实现类

      @Slf4j
      @Service
      public class BloomFilterServiceImpl implements BloomFilterService {
          private static final String CACHE_KEY_USER = "user:";
          @Resource
          private CheckUtils checkUtils;
          @Resource
          private RedisTemplate redisTemplate;
          @Autowired
          private IUserService userService;
          @Autowired
          private RedisCache redisCache;
          public void addUser(String phone) {
              //返回id
              User user = BeanUtil.copyProperties(UserDTO.builder().nickName("").build(), User.class);
              userService.save(user.setPhone(phone));
              // 这里可以开启一个异步线程,在事务提交之后再进行操作
              if (user.getId() > 0) {
                  String key = CACHE_KEY_USER + String.valueOf(user.getId());
                  //计算index位置
                  long index = checkUtils.getOffsetId(key);
                  // redis的数据都需要使用统一的json工具转成json格式后放入
                  redisCache.setCacheObject(key,user);
                  redisTemplate.opsForValue().setBit(BloomFilterInit.WHITELIST_USER_KRY, index, Boolean.TRUE);
                  log.info("新增用户信息|用户key:{}|布隆过滤器偏移量:{}", key, index);
              }
          }
          public User queryUser(Long id) {
              if (id < 0) {
                  log.info("获取用户信息|用户id异常,异常id:{}", id);
                  return null;
              }
              String key = CACHE_KEY_USER.concat(String.valueOf(id));
              boolean checkData = checkUtils.checkData(key);
              if (!checkData) {
                  log.info("获取用户信息|用户id不存在,异常id:{}", id);
                  return null;
              }
              //布尔过滤通过了!
              User user = redisCache.getCacheObject(key);
              log.info("用户信息 {}",user);
              //如果他为空
              if(Objects.isNull(user)) {
                  return null;
              }
              return user;
          }
      }
      

      我来先说这里的addUser的逻辑

      首先是直接到数据库中,存数据,这里的数据库的操作,可以自行换一个数据库,只要有id的就行

      然后就是存redis的过程

      先是获得redis的key 这里的key 拼接是这样 user: + id

      然后是获得index的位置,这个也是bitmap中的index

      存redis user用户

      存redis bitmap 设置为1

      queryUser

      先是获得key,先去查布隆过滤器,布隆过滤器的checkData

      这里的查找也是和设置bitmap的时候也是一样,就是去查找bitmap 在index位置是否是1

      如果通过,说明集合里边有他,就说明成功

      测试

      先添加用户

      Redis 布隆过滤器,在这里插入图片描述,第2张

      redis的样子

      Redis 布隆过滤器,在这里插入图片描述,第3张

      然后我们去查1017是否存在

      Redis 布隆过滤器,在这里插入图片描述,第4张

      Redis 布隆过滤器,在这里插入图片描述,第5张

      从这里看是存在的

      我们再去查1000

      是否存在

      Redis 布隆过滤器,在这里插入图片描述,第6张

      Redis 布隆过滤器,在这里插入图片描述,第7张

      这样就实现了简单的布隆过滤器

      总结

      总结来看,我这个小布隆过滤器,只有2^32个位置,而且还只是看一位的,所以蛮粗糙的,但是不妨碍我们理解布隆过滤器,不管他多复杂,思想都是一样的,都要去做hash的运算,算位置,比较位置,就没了

网友评论

搜索
最新文章
热门文章
热门标签
 
 周公解梦梦见被蛇缠身  梦见自己大便什么意思  梦见自己掉牙是怎么回事