Bloom Filter是一种空间效率很高的随机数据结构,它利用位数组很简洁地表示一个集合,并能判断一个元素是否属于这个集合。
Bloom Filter的这种高效是有一定代价的:在判断一个元素是否属于某个集合时,有可能会把不属于这个集合的元素误认为属于这个集合(false positive)。
因此,Bloom Filter不适合那些“零错误”的应用场合。而在能容忍低错误率的应用场合下,Bloom Filter通过极少的错误换取了存储空间的极大节省。
1.布隆过滤器设计原理
(1)包含m位的超大位数组(bitmap),每个值都是0;包含k个相互独立的哈希函数
(2)添加:将value进行y次(越大,准确率越高)哈希函数映射得到y个整数数值,以y个整数数值为下标修改bitmap位数组中值为1(如果本来就是1就不修改)
(3)判断:将searchValue进行y次哈希函数映射得到y个整数数值,然后在bigmap中找对应下标的值,
如果有下标数值不是1,结论:searchValue不在bitmap中
如果有下标数值都是1,结论:searchValue可能在bitmap中
2.redis+guava实现布隆过滤器
(1)redis环境安装
wget http://download.redis.io/releases/redis-5.0.8.tar.gz
tar -zxvf redis-5.0.8.tar.gz
mv redis-5.0.8 /usr/local
cd /usr/local
mv redis-5.0.8 redis
cd redis
make
make install
# 后台启动
nohup redis-server redis.conf &
# 连接
redis-cli -h 192.168.225.135 -p 6379
# 设置访问密码为123
config set requirepass 123
# 设置完成,需要输入密码才能操作数据
# 输入密码
auth 123
备注:redis重启数据丢失
vi /etc/sysctl.conf
# 增加一行
vm.overcommit_memory = 1
# 保存
:x
# 使配置文件生效
sysctl -p
(2)Java实现
org.springframework.boot
spring-boot-starter-data-redis
com.google.guava
guava
25.1-jre
@Bean
public BloomFilterHelper initBloomFilterHelper() {
return new BloomFilterHelper<>(
(Funnel) (from, into) -> into.putString(from, Charsets.UTF_8).putString(from, Charsets.UTF_8),
1000000, // 集合大小
0.01); // 错误率
}
/**
* 初始化BloomFilter
* @param funnel
* @param expectedInsertions
* @param fpp
*/
public BloomFilterHelper(Funnel funnel, int expectedInsertions, double fpp) {
Preconditions.checkArgument(funnel != null, "funnel不能为空");
this.funnel = funnel;
// 计算bit数组长度
bitSize = optimalNumOfBits(expectedInsertions, fpp);
// 计算hash方法执行次数
numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, bitSize);
}
/**
* 执行hash算法
* @param value
* @return
*/
public int[] murmurHashOffset(T value) {
int[] offset = new int[numHashFunctions];
long hash64 = Hashing.murmur3_128().hashObject(value, funnel).asLong();
int hash1 = (int) hash64;
int hash2 = (int) (hash64 >>> 32);
for (int i = 1; i <= numHashFunctions; i++) {
int nextHash = hash1 + i * hash2;
if (nextHash < 0) {
nextHash = ~nextHash;
}
offset[i - 1] = nextHash % bitSize;
}
return offset;
}
/**
* 计算bit数组长度
*/
private int optimalNumOfBits(long n, double p) {
if (p == 0) {
// 设定最小期望长度
p = Double.MIN_VALUE;
}
int sizeOfBitArray = (int) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
return sizeOfBitArray;
}
/**
* 计算hash方法执行次数
*/
private int optimalNumOfHashFunctions(long n, long m) {
int countOfHash = Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
return countOfHash;
}
3.使用redission实现布隆过滤器
org.redisson
redisson
3.13.1
RBloomFilter rBloomFilter = redissonClient.getBloomFilter(key);
boolean isSuccess = rBloomFilter.tryInit(expectedInsertions, fpp);
if (!isSuccess){
log.info("生成布隆过滤器失败");
throw new RuntimeException("生成布隆过滤器失败");
}
bloomFilter.add(str);
bloomFilter.contains(str);
测试方法
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
RedisTemplate redisTemplate = (RedisTemplate) context.getBean("redisTemplate");
redisTemplate.delete(key);
System.out.println("------test--start--------");
// RedisService redisService = context.getBean(RedisService.class);
BloomFilterService bloomFilterService = context.getBean(BloomFilterService.class);
System.out.println("------add--start--------");
Set set = new HashSet(1000);
List list = new ArrayList(1000);
for (int i = 0; i < 10000; i++) {
String uuid = UUID.randomUUID().toString();
if(i<1000){
set.add(uuid);
list.add(uuid);
}
// redisService.addByBloomFilter(key, uuid);
bloomFilterService.add(key, uuid);
}
for (int i = 0; i < 10; i++) {
String uuid = UUID.randomUUID().toString();
list.add(uuid);
}
System.out.println("------search--start--------");
for (String k: list){
System.out.println("-------------" + k + "--------------");
// System.out.println(redisService.includeByBloomFilter(key, k));
System.out.println(bloomFilterService.contains(key, k));
System.out.println("-------------" + k + "--------------");
}
}
4.使用RedisBloom插件实现布隆过滤器
4.1 插件与redis配置关联
# 下载
wget https://github.com/RedisBloom/RedisBloom/archive/v2.2.4.tar.gz
# 解压
tar -zxvf v2.2.4.tar.gz
# 进入解压目录之后make
make
# 成功后会生成一个 redisbloom.so 文件
# 切换到redis的目录中
vim redis.conf
# 修改 loadmodule 的值,配置 redisbloom.so 的存放位置
loadmodule /usr/local/redisBloom/redisbloom.so
4.2 在github中的介绍
https://github.com/RedisBloom/RedisBloom
https://github.com/RedisBloom/JRedisBloom
4.3 引入架包
com.redislabs
jrebloom
2.0.0-m2
4.4 配置maven中的setting
snapshots-repo
https://oss.sonatype.org/content/repositories/snapshots
4.5 Java实现
# 单台redis
Client client = new Client(new JedisPool(new JedisPoolConfig(), "192.168.225.135",6379, 500, "123"));
# 可以定制,也可以不写
client.createFilter("specialBloom", 10000, 0.0001);
# 集群分布式时
Set jedisClusterNodes = new HashSet<>();
// 地址集合
jedisClusterNodes.add(new HostAndPort("192.168.225.135", 6379));
ClusterClient client = new ClusterClient(jedisClusterNodes, 500, 500, 10, "123", new JedisPoolConfig());