[摘要]本篇文章给大家带来的内容是关于如何使用Redis的Bloomfilter去重,既用上了Bloomfilter的海量去重能力,又用上了Redis的可持久化能力,有一定的参考价值,有需要的朋友可以参考一...
本篇文章给大家带来的内容是关于如何使用Redis的Bloomfilter去重,既用上了Bloomfilter的海量去重能力,又用上了Redis的可持久化能力,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
前言:
“去重”是日常工作中会经常用到的一项技能,在爬虫领域更是常用,并且规模一般都比较大。去重需要考虑两个点:去重的数据量、去重速度。为了保持较快的去重速度,一般选择在内存中进行去重。
数据量不大时,可以直接放在内存里面进行去重,例如python可以使用set()进行去重。
当去重数据需要持久化时可以使用redis的set数据结构。
当数据量再大一点时,可以用不同的加密算法先将长字符串压缩成 16/32/40 个字符,再使用上面两种方法去重;
当数据量达到亿(甚至十亿、百亿)数量级时,内存有限,必须用“位”来去重,才能够满足需求。Bloomfilter就是将去重对象映射到几个内存“位”,通过几个位的 0/1值来判断一个对象是否已经存在。
然而Bloomfilter运行在一台机器的内存上,不方便持久化(机器down掉就什么都没啦),也不方便分布式爬虫的统一去重。如果可以在Redis上申请内存进行Bloomfilter,以上两个问题就都能解决了。
代码:
# encoding=utf-8import redisfrom hashlib import md5class 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) & retclass BloomFilter(object):
def __init__(self, host='localhost', port=6379, db=0, blockNum=1, 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)
self.bit_size = 1 << 31 # Redis的String类型最大容量为512M,现使用256M
self.seeds = [5, 7, 11, 13, 31, 37, 61]
self.key = key
self.blockNum = blockNum
self.hashfunc = [] for seed in self.seeds:
self.hashfunc.append(SimpleHash(self.bit_size, seed)) def isContains(self, str_input):
if not str_input: return False
m5 = md5()
m5.update(str_input)
str_input = m5.hexdigest()
ret = True
name = self.key + str(int(str_input[0:2], 16) % self.blockNum) for f in self.hashfunc:
loc = f.hash(str_input)
ret = ret & self.server.getbit(name, loc) return ret def insert(self, str_input):
m5 = md5()
m5.update(str_input)
str_input = m5.hexdigest()
name = self.key + str(int(str_input[0:2], 16) % self.blockNum) for f in self.hashfunc:
loc = f.hash(str_input)
self.server.setbit(name, loc, 1)if __name__ == '__main__':""" 第一次运行时会显示 not exists!,之后再运行会显示 exists! """
bf = BloomFilter() if bf.isContains('http://www.baidu.com'): # 判断字符串是否存在
print 'exists!'
else: print 'not exists!'
bf.insert('http://www.baidu.com')
基于Redis的Bloomfilter去重,既用上了Bloomfilter的海量去重能力,又用上了Redis的可持久化能力,基于Redis也方便分布式机器的去重。在使用的过程中,要预算好待去重的数据量,则根据上面的表,适当地调整seed的数量和blockNum数量(seed越少肯定去重速度越快,但漏失率越大)。