布隆过滤器是一种空间效率极高的概率数据结构,用于测试一个元素是否是集合的成员。如果布隆过滤器返回 False
,则元素绝对不在集合中。如果返回 True
,则元素可能在集合中,但也可能是一个误报。布隆过滤器利用了多个不同的哈希函数对元素进行哈希,并将结果的位置在一个位数组上设置为 1
。
1. redis5.0以上版本的布隆过滤器实现(RedisBloom)
RedisBloom
是 Redis 的一个模块,提供了 Bloom Filter、Cuckoo Filter、Count-Min Sketch 和 Top-K 这些数据结构。这些数据结构特别适用于处理海量数据的场景,因为它们是空间有效和计算高效的。
1.1 安装 RedisBloom:
确保你已经安装了 Redis。然后,你可以从 RedisBloom 的 GitHub 存储库下载并构建它。
bash
git clone --recursive https://github.com/RedisBloom/RedisBloom.git
cd RedisBloom
make
这将生成一个名为 redisbloom.so
的共享对象文件。
1.2 运行 Redis 与 RedisBloom 模块:
bash
redis-server --loadmodule ./redisbloom.so
1.3 使用 Bloom Filter:
接下来,你可以使用 redis-cli 或任何 Redis 客户端库与 Redis 交互。
例如,使用 redis-cli:
bash
redis-cli
添加元素到 Bloom Filter:
bash
127.0.0.1:6379> BF.ADD myfilter item1
(integer) 1
检查元素是否存在:
bash
127.0.0.1:6379> BF.EXISTS myfilter item1
(integer) 1
127.0.0.1:6379> BF.EXISTS myfilter item2
(integer) 0
1.4 Python 示例:
要在 Python 中使用 RedisBloom,你需要安装 redis-py 和 redisbloom 客户端库。
bash
pip install redis redisbloom
然后,你可以这样使用:
python
import redis
from redisbloom.client import Client
client = Client(host='localhost', port=6379)
client.bfCreate('myfilter', errorRate=0.01, capacity=10000)
client.bfAdd('myfilter', 'item1')
print(client.bfExists('myfilter', 'item1')) # True
print(client.bfExists('myfilter', 'item2')) # False
2. redis5.0以下版本的布隆过滤器实现(手写实现)
2.1 安装必要的库:
bash
pip install redis bitarray
2.2 布隆过滤器实现:
python
from datetime import datetime
import redis
from hashlib import md5
class SimpleHash(object):
def __init__(self, cap, seed):
self.cap = cap
self.seed = seed
def hash(self, value):
ret = 0
for i in range(len(value)):
ret += self.seed * ret + ord(value[i])
return (self.cap - 1) & ret
class BloomFilter(object):
def __init__(self, host='10.9.50.171', port=6379, db=1, password='asmd888', key='bloomfilter'):
"""
:param host: the host of Redis
:param port: the port of Redis
:param db: witch db in Redis
:param blockNum: one blockNum for about 90,000,000; if you have more strings for filtering, increase it.
:param key: the key's name in Redis
"""
self.server = redis.Redis(host=host, port=port, db=db, password=password, decode_responses=True)
self.bit_size = 1 << 25 # Redis的String类型最大容量为512M,现使用4M,误报率为(九/一百万)
self.seeds = [5, 7, 11, 13, 31, 37, 61]
# self.seeds = [5, 7, 11, 13, 31]
self.key = key
self.hashfunc = []
for seed in self.seeds:
self.hashfunc.append(SimpleHash(self.bit_size, seed))
def isContains(self, str_input):
"""
# 判断是否存在
:param str_input:
:return:
"""
if not str_input:
return False
m5 = md5()
m5.update(str_input.encode())
str_input = m5.hexdigest()
ret = True
for f in self.hashfunc:
loc = f.hash(str_input)
bit_name = self.server.getbit(self.key, loc)
ret = ret & bit_name
return ret
def insert(self, str_input):
"""
添加新的值
:param str_input:
:return:
"""
m5 = md5()
m5.update(str_input.encode())
str_input = m5.hexdigest()
for f in self.hashfunc:
loc = f.hash(str_input)
self.server.setbit(self.key, loc, 1)
if __name__ == '__main__':
""" 第一次运行时会显示 not exists!,之后再运行会显示 exists! """
bf = BloomFilter()
old = datetime.now()
name = 'www.baidusasdfasf.asdd4564asd'
if bf.isContains(name): # 判断字符串是否存在
print('exists!')
else:
print('not exists!')
bf.insert(name)
new = datetime.now()
print(new - old)