Bloom Filter算法优化
- 基于Redis的Bloom Filter去重,利用Redis的String数据结构,但Redis的String最大只能512M,所以数据过大的时候,需要申请多个去重块。
- Bloom Filter对一个很长的字符串进行哈希映射时会出错,常误判为已经存在,所以我们进行一次压缩(MD5\Sha1)。
- 将去重队列和种子队列拆分到不同的机器上。
种子seed数量越少去重速度越快,但是漏失率越大。
Md5长度为128bit,SHA-1长度160bit。
from redis import StrictRedis
from hashlib import md5
class HashMap(object):
def __init__(self, m, seed):
self.m = m
self.seed = seed
def hash(self, value):
ret = 0
for i in range(len(value)):
ret += self.seed * ret + ord(value[i])
return (self.m - 1) & ret
class BloomFilter(object):
def __init__(self, server, key, blockNum=1, bit=30, hash_number=6):
self.m = 1 << bit
self.seeds = range(hash_number) # 种子
self.blockNum = blockNum # 去重块的数量
self.maps = [HashMap(self.m, seed) for seed in self.seeds] # 多个哈希函数
self.server = server # Redis连接对象
self.key = key # 键名
def exists(self, value):
if not value:
return False
m5 = md5()
m5.update(value.encode())
value_md5 = m5.hexdigest() # 将value进行md5加密
ret = True
key_name = self.key + \
str(int(value_md5[0:2], 16) % self.blockNum) # 存储的block位置
for map in self.maps:
offset = map.hash(value_md5) # hash md5加密后的数据
ret = ret & self.server.getbit(
key_name, offset) # 修改每次hash过后,指定block块的位置
return ret
def insert(self, value):
m5 = md5()
m5.update(value.encode())
value_md5 = m5.hexdigest() # 将vlaue进行md5加密
key_name = self.key + \
str(int(value_md5[0:2], 16) % self.blockNum) # 存储的block位置
for map in self.maps:
offset = map.hash(value_md5) # hash md5加密后的数据
self.server.setbit(key_name, offset, 1)
redis = StrictRedis(host='localhost', port=6379)
bf = BloomFilter(redis, 'hash_map', 3, 10, 1)
# bf.insert('asd')
print(bf.exists('asd'))
# bf.insert('Hellow')
# bf.insert('World')