布隆过滤器(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日

相关文章

  • 在Go语言程序中使用gojson来解析JSON格式文件

    要在Go语言程序中使用gojson解析JSON格式文件,你需要按照以下步骤操作: 步骤1 安装gojson工具 你需要先在计算机上安装gojson工具,可以通过以下命令安装: go get github.com/ChimeraCoder/gojson/gojson 步骤2 生成Go语言结构体 使用gojson工具,我们可以将JSON文件转换成Go语言结构体。…

    C 2023年5月23日
    00
  • Java8 Stream flatmap中间操作用法解析

    Java 8中添加的Stream API为我们提供了一种更加高效的数据处理方式,而flatMap作为中间操作,在Stream编程中也是非常常用的。 flatMap的作用 flatMap操作是将Stream中的每个元素都转化为其他Stream,然后将这些Stream合并成一个Stream。其作用是将Stream中的嵌套结构“打扁”,使Stream中的每个元素都…

    C 2023年5月22日
    00
  • c++重载运算符时返回值为类的对象或者返回对象的引用问题

    在c++中,我们可以通过运算符重载的方式来改变运算符的行为。其中,当重载运算符时,需要考虑返回值的类型。一般情况下,可以返回基本数据类型、指针、引用或者类的对象。而对于返回类的对象和返回对象的引用问题,需要特别注意,以下是详细的攻略: 返回类的对象 返回类的对象时,需要考虑内存的分配问题,因为函数结束后栈上的内存空间被释放。为了避免内存泄漏,需要使用new来…

    C 2023年5月23日
    00
  • 适合初学者练习的C语言实现三子棋小游戏

    适合初学者练习的C语言实现三子棋小游戏完整攻略 三子棋是一款简单的棋盘游戏,它的规则简单易懂,被广泛地应用于人机交互、智力测试等领域。下面是如何使用C语言实现三子棋小游戏的完整攻略: 步骤一:确定游戏规则 首先,我们需要确定游戏规则,确保实现的游戏规则正确,符合三子棋的规则,如: 游戏双方执黑子和白子 执黑子先走 棋盘为3 x 3 的方格状 玩家操作后棋子不…

    C 2023年5月23日
    00
  • C语言Easyx实现贪吃蛇详解

    C语言Easyx实现贪吃蛇详解 简介 贪吃蛇是经典的小游戏,此篇攻略详细讲解如何用C语言结合Easyx图形库实现贪吃蛇的效果。 准备工作 安装Easyx Easyx是一款基于C语言的图形库,在此之前需要先下载和安装Easyx。 代码框架 以下是整个贪吃蛇程序的代码框架: #include <graphics.h> //Easyx头文件,必须要含有…

    C 2023年5月23日
    00
  • C++学习之异常机制详解

    C++学习之异常机制详解 什么是异常机制 C++的异常机制可以帮助我们处理程序运行时可能出现的意外状况,而在这些意外状况中,有些可能无法在程序设计时被完全预见,这个时候异常机制就可以帮助我们在程序出现异常时,优雅地终止程序,同时保证程序的稳定性。 C++异常机制的使用 C++的异常机制通过 try 和 catch 块来实现,其中 try 块用来包含可能会抛出…

    C 2023年5月23日
    00
  • C++有限状态机实现计算器小程序

    C++有限状态机实现计算器小程序攻略 1. 什么是有限状态机? 有限状态机(FSM, Finite State Machine)是一种数学模型,它可以通过状态转移来描述一个系统的行为。在有限状态机中,系统从一个状态转移至另一个状态,这是通过一些输入(input)或者事件(event)来触发的。有限状态机包含三个要素: 状态集合 输入集合 状态转移 2. 怎样…

    C 2023年5月23日
    00
  • C++14新特性的所有知识点全在这

    C++14新特性的所有知识点全在这 1. 简介 C++14是C++11的后继版本,引入了许多新的特性和性能改进。这些新特性使得C++14更容易使用和更加安全。本文将会介绍C++14的所有知识点。 2. C++14的新特性 2.1 通用表达式 通用表达式是C++14的一个重要特性,它提供了一种新的语法来实现编译时计算。通用表达式使得编程人员可以在编译时期计算变…

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