聊一聊布隆过滤器(Bloom filter)

什么是布隆过滤器?


布隆过滤器(Bloom Filter)是1970年由布隆提出的一个概念。它实际上是一个很长的二进制向量和一系列随机映射函数。事实上布隆过滤器可以理解为是哈希表的一种变式数据结构。

原理解析


正如介绍所说,布隆过滤器由两部分组成:二进制向量和随机映射函数。

  • 二进制向量:由二进制位组成的一个数组,数组的每个元素都是二进制位。

  • 随机映射函数:将数据映射到二进制向量的某一位或者多位中。

引入这样一个场景:

一个网站要添加URL黑名单,需要把包含20亿条URL的黑名单存储到计算机上,怎么存?怎么快速判断浏览器输入的url是否在黑名单中?

如果用布隆过滤器来存储,二十亿条URL映射成 bit 肯定在这个范围内:0 ~ 2,147,483,647‬。那么二进制向量的大小就是这么大:2,147,483,647 / 8 / 1024 / 1024 = 256 M。

比如:get_bit(url) = 4,那么二进制向量从低到高第四比特就置一。以此类推,将20亿条URL可以全部映射到这个二进制向量中,这样占用的存储空间才256M。如果用一般的哈希表,那么理论上需要的空间是:20亿*4 /1024 / 1024 / 1024 = 7.45G。

这个简单的对比就可以看出在某些场景使用布隆过滤器占用的空间非常少,这样小量的空间可以一次全部加载到内存中,不用读写磁盘,查找效率就会更高。

示意图:

blool filter demo

判断这个某个URL是否存在于黑名单中的方法,只要检测这个URL映射出来的那一位在二进制向量中是否为1。

  • 如果不为1,那么肯定不存在于黑名单;
  • 如果为1,那么这个URL“可能”存在于黑名单。

为什么是可能呢?因为哈希函数存在哈希碰撞的概率,可能会有其他的URL通过随机映射函数映射出来的位也是4。

这个问题目前还没有完全的解决方法,但是可以采用多次映射的方法来减小碰撞的概率。比如:通过多个随机映射函数映射到二进制向量上的多个位。如果这些位没有全部为1,那么这个URL肯定不存在于黑名单中;如果这些位都为1,那么这个URL存在于黑名单中的概率相比于之前的映射方法就大大提高了。

优缺点分析


由于布隆过滤器存储的是二进制向量,即以bit为单位来存储,所以它的空间占用相比于一般的哈希表就要小很多。而它又具有哈希表的性质,所以它的查找时间效率也很高。所以它的优点可以概括为以下几点:

  • 查找时间效率很高
  • 占用的空间很少,小空间可以直接在内存操作,更加提高了查找效率

但是由于它本身设计的限制(存在哈希碰撞),导致一般意义上的布隆过滤器存在”误判“的可能,同时也不好做删除操作:

  • 存在”误判“可能
  • 删除元素不方便

应用场景


Google著名的分布式数据库Bigtable以及Hbase使用了布隆过滤器来查找不存在的行或列,以减少磁盘查找的IO次数。

Google chrome 浏览器使用bloom filter识别恶意链接。

……

当我们有如下业务场景,就可以考虑使用布隆过滤器了:

  • 设置黑名单

  • 爬虫-URL去重

  • 单词拼写检查

  • Key-Value缓存系统的Key校验

  • ID校验,比如订单系统查询某个订单ID是否存在,如果不存在就直接返回。

  • ……

实战:C-实现布隆过滤器接口


BitMap.h

#pragma once 
//整型数据映射方法:2 ^ 32 / 8(字节) = 2 ^ 29->512M

//(2 ^ 32种状态,每个比特位表示一个状态,8个bit位为一个字节)

typedef unsigned long size_l;

typedef struct BitMap {
   
	size_l* _table;
	size_l _capacity;
	size_l _size;
}BitMap;

void InitBitMap(BitMap* bmp, size_l size);//初始化位图
//将指定位置数据设置为1,返回0,说明设置失败,返回1,说明设置成功
int Set(BitMap* bmp, size_l which);
//将指定位置数据设置为0,返回-1,说明设置失败,否则说明设置成功
int ReSet(BitMap* bmp, size_l which);

size_l SizeBitMap(BitMap* bmp);//求位图的容量
size_l CountBitMap(BitMap* bmp);//统计目前位图中有多少个数据
//判断指定位置的数据是否存在,返回1,说明数据存在,返回0,说明数据不存在,返回-1,说明判断失败
int TestPosBit(BitMap* bmp, size_l which);
void DestoryBitMap(BitMap* bmp);

BitMap.c

#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <malloc.h>
#include <stdlib.h>

#include "BitMap.h"

void InitBitMap(BitMap* bmp, size_l size) {
   
	assert(bmp);
	bmp->_capacity = (size >> 5) + 1;
	bmp->_table = (size_l*)malloc(sizeof(size_l) * bmp->_capacity);
	assert(bmp->_table);
	bmp->_size = size;
	memset(bmp->_table, 0, sizeof(size_l) * bmp->_capacity);
}

int Set(BitMap* bmp, size_l which) {
   
	if (NULL == bmp) {
   
		return 0;
	}
	if (which >= bmp->_size) {
   
		return 0;
	}
	
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值