布隆过滤器(bloom filter)及php和redis实现布隆过滤器的方法

布隆过滤器及实现方法攻略

什么是布隆过滤器?

布隆过滤器是一种非常实用的数据结构,它可以用于快速判断一个元素是否在一个集合中。布隆过滤器可以有效地降低查询一个元素是否在集合中的时间复杂度,但是会带来一定的误判率。它由早在1970年提出,以其高效的查询速度和内存占用率低的特点而广受欢迎,被广泛应用于网络爬虫等场景中。

布隆过滤器的实现原理

布隆过滤器采用的是概率算法,基于哈希函数和位图实现。它的核心思想是:开辟一个足够大的位图,对于每个元素,将其经过多个哈希函数得到的哈希值所对应的位置在位图上标记为1。当需要判断某个元素是否在集合中时,将其经过同样的哈希函数得到的哈希值所对应的位图位置查询一遍,如果所有的位都是1,则认为该元素在集合中。

布隆过滤器的应用场景

布隆过滤器可以用于以下场景:

  1. 网络爬虫的URL去重,防止重复采集;
  2. 黑名单过滤,例如垃圾邮件过滤等;
  3. 预判缓存命中,避免缓存miss带来的性能损失。

在这些场景中,布隆过滤器的作用都是用来减少需要进行高代价的副作用操作,例如网络爬虫需要访问目标网站进行URL去重,较大规模的黑名单过滤等。

PHP实现布隆过滤器

PHP实现布隆过滤器比较简单,我们可以使用常见的位图算法,将标记位存储在一个整数中。下面是一个PHP版的布隆过滤器代码:

class BloomFilter
{
    public static $hashFuncList = [
        // 自定义哈希函数,可以维护多个哈希函数。
        'hash1',
        'hash2',
    ];

    protected $bitmap = [];

    public function __construct($size = 1024)
    {
        $this->bitmap = array_fill(0, $size, 0);
    }

    public function add($word)
    {
        foreach(self::$hashFuncList as $func) {
            $hash = $this->$func($word);
            $idx = $hash % count($this->bitmap);
            $this->bitmap[$idx] = 1;
        }
    }

    public function exists($word)
    {
        foreach(self::$hashFuncList as $func) {
            $hash = $this->$func($word);
            $idx = $hash % count($this->bitmap);
            if($this->bitmap[$idx] != 1) {
                return false;
            }
        }
        return true;
    }

    protected function hash1($str) 
    {
        // 自定义哈希函数
        return hash('md5', $str);
    }

    protected function hash2($str) 
    {
        // 自定义哈希函数
        return hash('sha256', $str);
    }
}

在这个实现中,我们使用了一个 $hashFuncList 数组来维护多个哈希函数。每次添加或查找元素时,我们都会对这个元素进行多次哈希,并在对应位置上打标记。查询元素是否在集合中时,需要将其对应的位置都查询一遍,如果所有位置都被打标记,则该元素可能在集合中。

Redis实现布隆过滤器

在Redis中,我们可以使用自带的 bitmap 数据类型实现布隆过滤器,不需要再手写一个简单的位图算法了。其实现方式与PHP类似,仍然是使用多个哈希函数对元素进行哈希,并将结果在位图上标记为1。下面是一个Redis版的布隆过滤器代码:

class RedisBloomFilter
{
    protected $redis = null;

    public function __construct($redis)
    {
        $this->redis = $redis;
    }

    public function add($word)
    {
        foreach(['hash1', 'hash2'] as $func) {
            $hash = $this->$func($word);
            $this->redis->setbit('bloom', $hash, 1);
        }
    }

    public function exists($word)
    {
        foreach(['hash1', 'hash2'] as $func) {
            $hash = $this->$func($word);
            if($this->redis->getbit('bloom', $hash) == 0) {
                return false;
            }
        }
        return true;
    }

    protected function hash1($str)
    {
        return hexdec(substr(md5($str), 0, 8)) % 100000;
    }

    protected function hash2($str)
    {
        return hexdec(substr(md5($str), 8, 8)) % 100000;
    }
}

在这个实现中,我们使用了Redis自带的setbitgetbit命令分别用于设置和读取位图上的某一位。当需要添加或查询一个元素时,我们需要依次调用两个自定义的哈希函数来得到需要修改或查询的位图偏移量,并将其设置为1或查询其是否为1。

示例应用

下面我们来简单了解一下如何应用布隆过滤器解决具体问题:

防止缓存穿透

当缓存中不存在某个key时,我们需要向后端存储系统请求数据并将其缓存到缓存中。但在这个时候,有可能会出现频繁请求一个不存在的key的情况,这就会导致不断地向后端请求和缓存未命中的key,这就是缓存穿透问题。如果数据规模很大,那么查询后端存储系统的代价就会很大。这个时候我们就可以使用布隆过滤器来解决这个问题:

$bloom = new BloomFilter(100000);
$key = $_GET['key'];

if(!$bloom->exists($key)) {
  // key可能不存在,需要进一步查询后端存储系统
  // ...

  // 查询结果放到缓存中,并将key加入布隆过滤器中
  $bloom->add($key);
}

URL去重

在爬虫过程中,经常需要处理大量的URL,如果不进行去重,可能会大幅增加爬虫工作量。布隆过滤器既可以用来过滤已抓取或处理过的URL,也可以用来过滤黑名单URL。下面是一个基于Redis的示例代码:

$redis = new Redis();
$redis->connect('127.0.0.1', 6379, 600);
$bloom = new RedisBloomFilter($redis);

function isDuplicateUrl($url) {
  global $bloom;
  if ($bloom->exists($url)) {
    return true; // 该URL已经被处理过了
  }
  // 根据URL进行一些处理,并把处理后的结果存入缓存中
  // ...

  // 将处理过的URL加入布隆过滤器缓存中
  $bloom->add($url);
  return false;
}

在这个实现中,我们在爬虫过程中使用isDuplicateUrl函数判断当前URL是否已经被处理过,如果已经被处理过则直接跳过,否则将其加入Redis布隆过滤器并进行处理。这样,就能够有效地去重URL并避免重复处理。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:布隆过滤器(bloom filter)及php和redis实现布隆过滤器的方法 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • Java实现学生成绩管理系统

    Java实现学生成绩管理系统完整攻略 搭建环境1. 安装Java开发工具包(JDK)2. 安装Java集成开发环境(IDE),如Eclipse、IntelliJ IDEA等 设计数据库1. 使用MySQL等数据库软件创建“学生成绩管理系统”所需的数据库和表结构2. 数据库表设计包括学生信息表、课程信息表和成绩信息表 实现模型层代码1. 根据设计好的表结构,创…

    C 2023年5月23日
    00
  • C++表达式new与delete知识详解

    C++表达式new与delete知识详解 在 C++ 中,new 和 delete 是用于动态分配内存的表达式。new 用于分配内存,delete 用于释放内存。 new 表达式 基本语法 pointer = new type; 其中,pointer 是指向新的分配的内存空间的指针,type 是数据类型。new 表达式会分配一个存储类型为 type 的对象的…

    C 2023年5月22日
    00
  • 5A的过电流能力到底如何?华为Mate 9原装Type-C数据线拆解

    5A的过电流能力到底如何? 什么是过电流保护? 过电流保护是指在设备工作中,当电流流过该设备时,如果电流大小超出设备本身设计的工作范围时,设备会自动断开电流通路,来保护设备不受到电流侵害。 5A的过电流能力如何实现? 在华为Mate 9原装Type-C数据线中,实现5A过电流能力的关键就是使用了特殊的电子元器件,这些元器件能够支持高电流载流量,并具有快速反应…

    C 2023年5月23日
    00
  • c++11 新特性——智能指针使用详解

    C++11 新特性——智能指针使用详解 在C++中,内存管理一直是一个非常重要的事情,一个常见的错误就是忘记释放先前分配的内存。C++11引入了智能指针,从而使得内存管理更加方便。本文将详细介绍智能指针的使用方法。 智能指针概述 C++中的智能指针是一种RAII(Resource Acquisition Is Initialization)机制的实现,它通过…

    C 2023年5月22日
    00
  • 如何用C语言编写PHP扩展的详解

    如何用C语言编写PHP扩展的详解 一个PHP扩展是由C语言写的动态链接库,它可以用来扩展PHP的功能,提高PHP代码的性能。编写PHP扩展可以让我们在PHP代码中使用C语言提供的高效、强大的功能,并且可以与PHP代码无缝集成。 编写PHP扩展的详细流程如下: 准备环境 在开始编写PHP扩展之前,需要准备好下面的环境: PHP源代码(需要与扩展编写的PHP版本…

    C 2023年5月23日
    00
  • C语言实现2048游戏代码

    C语言实现2048游戏代码攻略 一、项目背景 2048游戏是一款非常经典且受欢迎的益智类游戏,目前已经在各个平台上得到广泛的应用。实现2048游戏的过程既可以锻炼编程基础功底,还能提高逻辑思维能力。因此,本项目旨在利用C语言实现2048游戏代码,供初学者参考与学习。 二、实现步骤 1. 初始化棋盘 首先,我们需要在C语言中创建一个数组,并将所有元素初始化为0…

    C 2023年5月23日
    00
  • C语言指针多层间接引用

    当需要对指针类型的变量进行多次操作时,可以使用多层间接引用方式,也称为指针嵌套,下面就对C语言指针多层间接引用进行详细讲解。 1.什么是指针多层间接引用 指针的多层间接引用就是指针指向指针,这些指针有时会指向更多的指针,直到最后指向某个特定的值。这个过程就是多层间接引用,也就是指针嵌套的过程。 2.多层指针的定义 定义多层间接引用的指针需要使用一对或多对星号…

    C 2023年5月9日
    00
  • C语言实现考试报名管理系统

    C语言实现考试报名管理系统攻略 系统介绍: 本系统使用C语言编写,实现了考试报名管理系统,可以方便地管理考试的报名、查询与统计工作。 系统功能: 学生信息管理:系统中可以管理考生信息,包括学生姓名、学号、报考考试、成绩等信息。 考试报名:考生可以通过登录系统进行报名。 考试查询:考生和管理员根据个人信息可以查询自己或其他考生的成绩,并且管理员可以查看全体考生…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部