一、介绍
是一种用于快速检查一个元素是否存在于一个集合中的概率型数据结构。它基于哈希函数和位数组实现,通常用于在大规模数据集中快速检索元素是否存在,尤其是在需要快速判断某个元素是否可能存在时,可以提供高效的近似查询。
优点:
-
快速查询:布隆过滤器可以在常数时间内判断一个元素是否可能存在于集合中,不受集合大小的影响, 因此适用于处理大规模数据集合 。
-
空间效率高:相比于其他数据结构,布隆过滤器在存储相同数量的元素时,所需的空间通常较小, 因为它只需要存储元素的哈希值,而不需要存储实际元素 。
-
简单高效:布隆过滤器的实现相对简单,只需要使用哈希函数和位数组即可,不涉及复杂的数据结构和算法。
缺点:
-
误判率:由于布隆过滤器是基于概率的数据结构,存在一定的误判率,即可能将一个不存在的元素误判为存在. ,可能会出现误判(即判断元素存在但实际不存在的情况),但不会出现漏判(即判断元素不存在但实际存在的情况)。误判的概率取决于布隆过滤器的大小和哈希函数的数量。 。
-
不支持删除操作:布隆过滤器一般不支持元素的删除操作,因为删除一个元素会影响到其他元素的判断结果。
-
无法存储额外信息:布隆过滤器只能判断元素是否存在,无法存储额外的信息,如元素的具体值或其他相关信息。
应用场景:
-
缓存优化:用于快速判断某个数据是否存在于缓存中,避免缓存穿透问题。
-
数据过滤:用于过滤垃圾邮件、恶意网址等,减少不必要的计算和存储开销。
-
分布式系统:用于快速判断某个数据是否存在于分布式系统中的某个节点,减少网络通信开销。
-
大数据处理:用于快速判断某个数据是否可能存在于海量数据中,提高查询效率。
二、布隆过滤器详解
海量数据处理以及缓存穿透这两个场景让我认识了布隆过滤器 ,我查阅了一些资料来了解它,但是很多现成资料并不满足我的需求,所以就决定自己总结一篇关于布隆过滤器的文章。希望通过这篇文章让更多人了解布隆过滤器,并且会实际去使用它!
一、什么是布隆过滤器?
1.1 概念
布隆过滤器(Bloom Filter)是一个叫做 Bloom 的老哥于 1970 年提出的。我们可以把它看作由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构。
相比于我们平时常用的的 List、Map 、Set 等数据结构,它占用空间更少并且效率更高,但是缺点是其返回的结果是概率性的,而不是非常准确的。理论情况下添加到集合中的元素越多,误报的可能性就越大。并且,存放在布隆过滤器的数据不容易删除。
BIT数组
0
位数组中的每个元素都只占用 1 bit ,并且每个元素只能是 0 或者 1。这样申请一个 100w 个元素的位数组只占用 1000000Bit / 8 = 125000 Byte = 125000/1024 kb ≈ 122kb 的空间。
1.2 总结
一个名叫 Bloom 的人提出了一种来检索元素是否在给定大集合中的数据结构,这种数据结构是高效且性能很好的,但缺点是具有一定的错误识别率和删除难度。并且,理论情况下,添加到集合中的元素越多,误报的可能性就越大。
二、布隆过滤器的原理介绍
2.1 前置条件
●若干个哈希函数,哈希函数的数量决定布隆过滤器的精度。
●位数组:数字元素类型为 Bit
2.2 数据新增时逻辑
步骤一:使用布隆过滤器中的哈希函数对元素值进行计算,得到哈希值(有几个哈希函数得到几个哈希值)。
步骤二:将步骤一计算得到的哈希值作为数组下标,在【位数组】中把对应下标的值置为 1。
2.3 数据查询时逻辑
步骤一:对给定元素使用前置条件中的那些哈希函数进行哈希计算。
步骤二:将步骤一计算得到的哈希值作为数组下标,在【位数组】中查看对应下标的值如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。
2.4 流程梳理
助消化:你应该会好奇为什么需要这么多 hash 函数,每个 hash 函数负责计算不同区域数组下标,在一定范围内 hash函数越多那么出现误判的可能性越小。可以把 hash函数当成比赛晋级的评委,只有参与投票的所有评委都同意才可以晋级,显然评委的数量越多含金量越大。
如上图所示,当字符串存储要加入到布隆过滤器中时,该字符串首先由多个哈希函数生成不同的哈希值,然后将对应的位数组的下标设置为 1(当位数组初始化时,所有位置均为 0)。当第二次存储相同字符串时,因为先前的对应位置已设置为 1,所以很容易知道此值已经存在(去重非常方便)。如果我们需要判断某个字符串是否在布隆过滤器中时,只需要对给定字符串再次进行相同的哈希计算,得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。不同的字符串可能哈希出来的位置相同,这种情况我们可以适当增加位数组大小或者调整我们的哈希函数。综上,我们可以得出以下结论:布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。
三、布隆过滤器使用场景
判断给定数据是否存在:比如判断一个数字是否存在于包含大量数字的数字集中(数字集很大,5 亿以上!)防止缓存穿透(判断请求的数据是否有效避免直接绕过缓存请求数据库)等等、邮箱的垃圾邮件过滤、黑名单功能等等。
去重:比如爬给定网址的时候对已经爬取过的 URL 去重。
Charset charset = Charset.forName("UTF-8"); BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(charset), 1000000, 0.01); boolean put = bloomFilter.put("userId:1"); boolean b = bloomFilter.mightContain("userId:1"); // BloomFilter<String> integerBlomFilter = BloomFilter.create(Funnels.stringFunnel() 1000000, 0.01); // boolean b = integerBloomFilter.mightContain(1); // for (int i = 0; i < 1000000; i++) { // integerBloomFilter.put(i); // } // int count=0; // for (int i = 1000000; i < 1000000+100000; i++) { // boolean b = integerBloomFilter.mightContain(i); // if(b){ // count++; // } // } // System.out.println(b);
-
BloomFilter
:这里声明了一个布隆过滤器对象,它被参数化为存储整数(Integer)类型的元素。 -
BloomFilter.create(...)
:这是创建布隆过滤器的静态工厂方法。它接受三个参数:-
Funnels.integerFunnel()
:这是一个用于序列化整数的 Funnel 对象。Funnel 定义了如何将对象转换为一系列字节,用于布隆过滤器的哈希计算。 -
1500
:这是预期插入的元素数量。布隆过滤器会预先分配一定数量的位(bits)用于表示元素是否存在,这个参数指定了预期插入的元素数量,用于计算布隆过滤器的大小。 -
0.01
:这是期望的误报率(false positive rate)。布隆过滤器会根据预期的插入数量和误报率计算所需的位数组大小和哈希函数数量。误报率是指在元素不在布隆过滤器中的情况下,仍然有多少概率会被误判为存在。这个值越小,布隆过滤器所需的位数组大小和哈希函数数量就越大,但误报率也会降低。
-
通过这段代码,你创建了一个能够存储整数类型的布隆过滤器,预计最多能插入 1500 个元素,并且期望的误报率为 0.01。
HttpClient 是一个流行的 Java HTTP 客户端库,它提供了丰富的功能来方便地进行 HTTP 请求和处理响应。下面详细解释一下 HttpClient 的一些重要特性和用法:
特性:
-
易用性: HttpClient 提供了简单易用的 API,使得发送 HTTP 请求和处理响应变得容易。
-
功能丰富: HttpClient 提供了发送各种类型的 HTTP 请求的方法,包括 GET、POST、PUT、DELETE 等,也支持设置请求头、请求参数、处理响应等操作。
-
连接管理: HttpClient 可以管理 HTTP 连接池,提高连接的复用率和性能。
-
状态管理: HttpClient 可以处理 HTTP 状态码,支持重定向、身份验证等功能。
-
异步请求: HttpClient 支持发送异步请求,可以提高并发性能。
用法:
-
创建 HttpClient 实例: 使用
HttpClients.createDefault()
或者自定义配置创建一个CloseableHttpClient
实例。CloseableHttpClient httpClient = HttpClients.createDefault();
-
创建请求对象: 根据请求类型创建相应的请求对象,如
HttpGet
、HttpPost
等。HttpGet httpGet = new HttpGet("http://example.com");
-
设置请求参数和头部信息(可选): 可以通过方法设置请求参数、请求头等信息。
httpGet.addHeader("User-Agent", "Mozilla/5.0");
-
发送请求并处理响应: 使用 HttpClient 实例的
execute
方法发送请求,并获取响应对象。CloseableHttpResponse response = httpClient.execute(httpGet);
-
处理响应结果: 从响应对象中获取响应状态码、响应头、响应体等信息。
int statusCode = response.getStatusLine().getStatusCode(); String responseBody = EntityUtils.toString(response.getEntity());
-
关闭连接: 在使用完毕后,记得关闭响应对象和 HttpClient 实例,释放资源。
response.close(); httpClient.close();
以上是一个简单的 HttpClient 使用流程,您可以根据实际需求进行进一步的定制和扩展。HttpClient 还支持设置超时时间、自定义请求和响应拦截器、处理 SSL、代理设置等功能,以满足不同的需求场景。