目录
前言
想必大家都知道布隆过滤器,它是为了预防黑客发起大量非法请求的一种手段,例如Redis的缓存穿透,就可以用布隆过滤器来解决。它最大的特点是说没有的一定没有,说有的不一定有。
忘记redis的缓存问题的小伙伴可以回顾一下:老掉牙的面试点:缓存击穿、穿透、雪崩、倾斜的问题及解决方案(简洁易懂,一篇搞定!)_无聊写写写的博客-CSDN博客
一、什么是布隆过滤器(Bloom Filter)
布隆过滤器:1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列的随机映射函数(哈希函数)两部分组成的结构,用于快速检索一个元素是否可能存在于一个集合(bit数组中)。
优点:
- 时间复杂度低,增加及查询元素的时间复杂度都是O(k),k为Hash函数的个数;
- 占用存储空间小,布隆过滤器相对于其他数据结构(如Set、Map)非常节省空间。
缺点:
- 存在误判,只能证明一个元素一定不存在或者可能存在,返回结果是概率性的,但是可以通过调整参数来降低误判比例;
- 删除困难,一个元素映射到bit数组上的k个位置为1,删除的时候不能简单的直接置为0,可能会影响到其他元素的判断。
二、布隆过滤器的原理
它的基本原理是利用多个哈希函数,将一个元素映射成多个位,然后将这些设置为1。当查询一个元素时,如果这些位都被设置为1,则认为元素可能存在集合中,否则肯定不存在。
所以,布隆过滤器可以准确的判断一个元素是否一定不存在,但是因为哈希冲突的存在,所以他没办法判断一个元素一定存在,只能判断可能存在。
所以,布隆过滤器是存在误判的可能的,也就是当一个不存在“嘿嘿”元素,经过hash1、hash2和hash3之后,刚好和其他的值的哈希结果冲突了。那么就会误判为存在,但是其实它不存在。
想要降低这种误判的概率,主要的方法就是降低哈希冲突的概率及引入更好的哈希算法。
三、布隆过滤器的工作流程
1.初始化布隆过滤器
在初始化时,需要指定集合的大小和误判率。布隆过滤器内部包含一个bit数据和多个哈希函数,每个哈希函数都会生成一个索引值
2.添加元素到布隆过滤器
要将一个元素添加到布隆过滤器中,首先需要将该元素通过多个哈希函数生成多个索引值,然后将这些索引值对应的位设置为1。如果这些索引值已经被设置为1,则不需要再次设置。
3.查询元素是否存在布隆过滤器中
要查询一个元素是否存在于布隆过滤器中,需要将该元素通过多个哈希函数生成多个索引值,并判断这些索引值对应的位是否被设置为1.如果这些位被设置为1,则认为元素可能存在集合中,否则肯定不存在
四、布隆过滤器的实际应用场景
- 网页爬虫:爬虫程序可以使用布隆过滤器来过滤已经爬取的网页,避免重复爬取和浪费资源。
- 缓存系统:缓存系统可以使用布隆过滤器来判断一个查询是否可能存在于缓存中,从而减少查询缓存的次数,提高查询效率。
- 分布式系统:在分布式系统中,可以使用布隆过滤器来判断一个元素是否存在于分布式缓存中,避免在所有节点上进行查询,减少网络负载。
- 垃圾邮件过滤:判断一个邮件地址是否存在垃圾邮件列表中,从而过滤掉垃圾邮件。
- 黑名单过滤:判断一个IP地址或手机号是否在黑名单中,从而阻止恶意请求。
五、Java实现布隆过滤器
5.1、Guava
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import java.nio.charset.Charset;
public class GuavaExample {
public static void main(String[] args) {
//创建布隆过滤器,预计插入100个元素,误判率0.01
BloomFilter<String> bloomFilter=BloomFilter.create(
Funnels.stringFunnel(Charset.defaultCharset()),100,0.01);
//插入元素
bloomFilter.put("哈哈");
bloomFilter.put("嘿嘿");
bloomFilter.put("嘻嘻");
//判断元素是否存在
System.out.println(bloomFilter.mightContain("哈哈"));//true
System.out.println(bloomFilter.mightContain("啊啊"));//false
}
}
5.2、Redission
import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
public class RedisExample {
public static void main(String[] args) {
Config config=new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redissonClient = Redisson.create(config);
RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter("myfilter");
bloomFilter.tryInit(100,0.01);
//插入元素
bloomFilter.add("哈哈");
bloomFilter.add("嘿嘿");
bloomFilter.add("嘻嘻");
//判断元素是否存在
System.out.println(bloomFilter.contains("哈哈"));//true
System.out.println(bloomFilter.contains("啊啊"));//false
redissonClient.shutdown();
}
}
5.3、Apache Commons
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.collections4.BloomFilter;
import org.apache.commons.collections4.functors.HashFuntionIdentity;
public class ApacheExample {
public static void main(String[] args) {
BloomFilter<String> bloomFilter = new BloomFilter<>
(HashFunctionIdentity.hashFunction(StringUtils::hashCode), 100, 0.01)
//插入元素
bloomFilter.put("哈哈");
bloomFilter.put("嘿嘿");
bloomFilter.put("嘻嘻");
//判断元素是否存在
System.out.println(bloomFilter.mightContain("哈哈"));//true
System.out.println(bloomFilter.mightContain("呵呵"));//false
}
}
5.4、Jedis
import redis.clients.jedis.Jedis;
public class JedisExample {
public static void main(String[] args) {
Jedis jedis = new Jedis("locahost");
jedis.bfCreate("myfilter",100,0.01);
jedis.bfAdd("myfilter","哈哈");
jedis.bfAdd("myfilter","嘿嘿");
jedis.bfAdd("myfilter","呵呵");
System.out.println(jedis.exists("myfilter","哈哈"));//true
System.out.println(jedis.exists("myfiter","嘻嘻"));//false
}
}