bloomfilter笔记:基于Redis的布隆过滤器

数据过滤问题是爬虫项目开发时极为重要的一个环节

使用redis过滤的优点:速度快、可持久化

问题:当需要过滤的数据量过大(上亿)的时候即使搭配MD5(字符级)占用内存仍然比较大,

布隆过滤器优点:速度快、占用内存小(位级)

问题:不支持持久化,down机即失效

布隆过滤器的原理:https://blog.csdn.net/jiaomeng/article/details/1495500


解决方案:使用布隆过滤器的算法进行过滤,使用可持久化的redis来进行存储

bloomfilter实现代码:

# -*- coding: utf-8 -*-
import redis
from hashlib import md5


class SimpleHash(object):
    """
    1.根据种子生成hash函数
    2.根据hash函数将存储对象value映射在内存范围大小(bit_size)内的一个值
    """

    def __init__(self, bit_size, seed):
        # 开辟内存的大小
        self.bit_size = bit_size
        # 种子
        self.seed = seed

    def hash(self, value):
        """
        用于将存储对象映射到存储范围内的一个值
        :param value:
        :return:
        """
        ret = 0
        for i in range(len(value)):
            ret += self.seed * ret + ord(value[i])
        # 控制hashValue的值在这个内存空间的范围
        hash_value = (self.bit_size - 1) & ret
        # 返回映射到的值
        return hash_value


class BloomFilter(object):
    """
    在redis中初始化一个大字符串,也可以认为是在redis中开辟了一块内存空间
    """

    def __init__(self, host='localhost', port=6379, db=2, block_num=1, key='bloomfilter'):
        """
        :param host: redis主机
        :param port: redis端口
        :param db: redis的库
        :param block_num: 用来控制最大可过滤数量 每增加1(即会在redis多创建一个key)大概可扩展9000w
        :param key: 用于组成在redis中的key
        """
        self.server = redis.Redis(host=host, port=port, db=db)
        # 这是一个限制值,最大为256M,因为在redis中,字符串值可以进行伸展,伸展时,空白位置以0填充。
        self.bit_size = 1 << 31  # Redis的String类型最大容量为512M,现使用256M
        # 种子列表
        self.seeds = [5, 7, 11, 13, 31, 37, 61]
        self.key = key
        # 用来控制最大可过滤数量 每增加1(即会在redis多创建一个key)大概可扩展9000w
        self.block_num = block_num
        # 根据种子列表生成对应的的hash函数
        self.hash_func = [SimpleHash(self.bit_size, seed) for seed in self.seeds]

    def exists(self, item):
        """
        判断存储对象是否在过滤器内
        :param item: 存储对象
        :return:
        """
        m5 = md5()
        m5.update(item.encode('utf-8'))
        # 取存储对象的md5指纹
        item = m5.hexdigest()
        # redis中对应的key
        name = self.key + str(int(item[0:2], 16) % self.block_num)
        # 获取item通过hash函数列表中的每个函数映射到的位置的值,如果有一个位置的值为0即判定不存在
        result = filter(lambda f: self.server.getbit(name, f.hash(item)) == 0, self.hash_func)
        return result == []

    def insert(self, item):
        """
        将item添加到过滤器
        即将item通过 种子列表生成的hash函数 映射到的每个位置的值都置为1
        :param item: 存储对象
        :return:
        """
        m5 = md5()
        m5.update(item.encode('utf-8'))
        # 将存储对象生成MD5指纹
        item = m5.hexdigest()
        # 存储在redis中的key
        name = self.key + str(int(item[0:2], 16) % self.block_num)
        # 将存储对象通过hash函数列表中的每个函数映射的位置的值都置为1
        map(lambda f: self.server.setbit(name, f.hash(item), 1), self.hash_func)


if __name__ == "__main__":
    bf = BloomFilter()
    if bf.exists('https://www.baidu.com/'):
        print('url is exist!')
    else:
        print('url not exist!')
        bf.insert('https://www.baidu.com/')

代码参考:https://blog.csdn.net/bone_ace/article/details/53107018

猜你喜欢

转载自blog.csdn.net/qq_41864652/article/details/81136861