bitmap
bitmap是redis中的一个数据结构,是一个二进制数组;由于二进制的值只能是0或者1,所以bitmap更多存的还是某个元素是否存在这个数组中;
布隆过滤器
组成
由bitmap(二进制数组)与hash算法组成;
当一个元素存入到布隆过滤器中时,先将该元素通过hash算法算出一个hash值,用这个hash值与bitmap的长度取模,算到的数据肯定是小于这个bitmap的长度的,然后这个值就是该元素存在于布隆过滤器中的下标位置,将这个位置的值设置为1;
问题
由于主要是通过了hash算法,所以肯定是会遇到hash冲突的问题;hash冲突的问题就是,假设布隆过滤器中的bitmap的长度为12,一个数字6存进去,通过hash算法也算出来6,那6/12表示在下标为6的位置设置成1,这个时候一个数字18进来,想看是否在这个布隆过滤器中存在,但是18/12也等于6,那么虽然18不在数组中,但是查出来的结果是1,所以就可能出现误判;但是如果通过计算,该元素所在位置的值为0,说明这个元素肯定不在这个数组中。
解决办法
1.增加bitmap数组长度,这样分母变大,hash冲突的概率就低,缺点是太耗内存,不可取;
2.用多个hash函数,将元素多进行几次hash计算,获得不同的hash值,每个hash值都与bitmap长度取模,取到多个下标,都设置成1,这样不同的数计算完下标之后,值一模一样的概率就会小很多。
使用
简单点可以使用redis+redission实现:
构造一个RedissionClient,通过redission.getBloomFilter(key)获得一个RBloomFilter<String>实例,接着初始化这个实例:bloomFiltrer.tryInit(100000000L,0.03)(第一个数字代表bitmap数组长度,第二个浮点型代表允许的误差是3%,源码会根据这个误差去算需要多少个hash函数),然后用这个布隆过滤器实例完成塞值操作:bloomFilter.add(vlue),判断当前元素是否存在与布隆过滤器中,用代码:bloomFilter.contains(key)实现,返回true则代表存在,这时候可以去查缓存,缓存中没有就查数据库,返回false代表这个元素肯定不存在系统中,可以直接拒绝请求了。
还可以通过guava实现无redis布隆过滤器,只是因为是代码实现,分布式中肯定不能使用。
总结
布隆过滤器可以有效解决缓存穿透的问题;应用场景有黑名单、邮件垃圾过滤等等;