• Redis 布隆过滤器的原理和实践


    一、简介

    1 布隆过滤器的定义

    布隆过滤器是一种空间效率高、误判率可控的数据结构,通常用于检索一个元素是否在一个集合中。它是由一个比特向量和多个哈希函数组成的。布隆过滤器可以用于快速检测一个元素是否存在于一个集合中,其主要优点是省内存缺点是有一定的误识别率和删除困难。

    2 Redis 布隆过滤器的特点

    • Redis 布隆过滤器采用了 Redis 自身的数据结构实现,支持数据持久化,重启后依然有效
    • 在 Redis 中使用布隆过滤器需要先安装 RedisBloom 模块
    • Redis 布隆过滤器使用多个哈希函数,并使用不同的随机种子在一定程度上降低了误判率
    • Redis 布隆过滤器可以设置错误率和元素数量通过调整这些参数可以控制误判率

    3 Redis 布隆过滤器的应用场景

    • 邮箱黑名单、敏感词过滤
    • 在搜索引擎中过滤掉爬虫或者恶意软件等
    • 在数据库中避免重复插入相同数据
    • 在网站访问中,可以使用布隆过滤器缓存那些已经访问过的页面

    二、原理分析

    1 布隆过滤器的基本原理

    假设哈希函数个数为 k 每个比特位被设置多少次记为 m,n 为元素数量,则误判率为 (1 - e(-kn/m))^k。当误判率为 p 时选取最优的哈希函数个数 k 和比特位长度 m 可以使空间利用更加高效

    代码示例

    public class BloomFilter {
    
        private int size;
        private int[] hashes;
    
        private BitSet bits;
    
        public BloomFilter(int size, int[] hashes) {
            this.size = size;
            this.hashes = hashes;
            this.bits = new BitSet(size);
        }
    
        public void add(String value) {
            for (int hash : hashes) {
                int position = Math.abs(value.hashCode() * hash) % size;
                bits.set(position, true);
            }
        }
    
        public boolean contains(String value) {
            for (int hash : hashes) {
                int position = Math.abs(value.hashCode() * hash) % size;
                if (!bits.get(position)) {
                    return false;
                }
            }
            return true;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    2 布隆过滤器的实现原理

    • 创建 Redis 布隆过滤器时需要指定误判率和预期元素数量,Redis 客户端会根据这些参数计算出最优的比特位长度 m 和哈希函数个数 k。
    • Redis 使用 MurmurHash2 和 MurmurHash64A 两个哈希函数计算各自的哈希值,来保证布隆过滤器的误判率和运行效率。
    • 将插入元素在 k 个哈希函数中生成对应的 k 个哈希值,并将比特向量的这些位置置为 1。
    • 查找元素是同样地使用 k 个哈希函数计算出 k 个哈希值,在比特向量的对应位置查找是否都为 1。

    代码示例

    Jedis jedis = new Jedis("localhost", 6379);
    jedis.getClient().sendCommand(BloomFilterCommands.RESERVE, "mybloom", "0.001", "100000");
    
    jedis.getClient().sendCommand(BloomFilterCommands.ADD, "mybloom", "hello");
    Response<Boolean> response = jedis.getClient().getBooleanReply();
    response.get();
    
    jedis.getClient().sendCommand(BloomFilterCommands.EXISTS, "mybloom", "hello");
    Response<Boolean> response = jedis.getClient().getBooleanReply();
    response.get();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3 布隆过滤器的优化策略

    • 增加哈希函数的数量可以降低误判率,但同时也会增加空间复杂度和计算时间
    • 确定适当的预期元素数量和误判率,避免过拟合和欠拟合
    • 在 Redis 集群中,布隆过滤器可以通过将多个小的布隆过滤器组成一个大的布隆过滤器来协同工作,提高并发处理能力和存储效率

    三、Redis 布隆过滤器的实践

    1 布隆过滤器的安装和配置

    Redis 布隆过滤器需要在 Redis 中进行安装和配置才能够使用。首先需要在 Redis 安装目录下的 src 文件夹中找到 redis-trib.rb 文件。

    # 进入 Redis 安装目录
    cd xxx/redis-xx
    
    # 执行以下命令安装 Redis 布隆过滤器
    ruby src/redis-trib.rb create --replicas 1 ip:port ip:port ip:port ...
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在上述命令中ip:port 需要替换为 Redis 节点的 IP 地址和端口号

    2 布隆过滤器的使用方法

    Redis 布隆过滤器常用于判断某个元素是否在集合中可以通过以下命令进行使用:

    # 向布隆过滤器添加元素
    BF.ADD key element [element ...]
    
    # 判断元素是否在布隆过滤器中
    BF.EXISTS key element
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其中,key 表示 Redis 的键名,element 表示需要添加或判断的元素。

    3 布隆过滤器的性能测试和优化

    Redis 布隆过滤器可以通过以下命令来进行性能测试:

    # 测试添加元素的速度
    BF.RESERVE key 0.0001 10000
    
    # 测试判断元素是否在布隆过滤器中的速度
    BF.EXISTS key element
    
    • 1
    • 2
    • 3
    • 4
    • 5

    需要注意的是在 BF.RESERVE 命令中,第一个参数 key 代表 Redis 的键名,第二个参数为错误率即误报的概率,第三个参数代表预期最大元素个数。

    如果发现 Redis 布隆过滤器性能较低,可以通过增加节点个数或降低错误率等方式进行优化。

    四、Redis 布隆过滤器的应用案例

    1 布隆过滤器在网站防刷中的应用

    Redis 布隆过滤器可以用于网站的防刷功能,通过判断IP地址或者用户行为是否已经超出一定的次数来避免短时间内多次请求相同的资源。例如:

    # 判断 IP 地址是否已经被封禁
    if BF.EXISTS ip_bloom_filter ip {
        return 403; # 返回禁止访问的状态码
    }
    
    • 1
    • 2
    • 3
    • 4

    2 布隆过滤器在数据去重中的应用

    在数据去重方面可以使用 Redis 布隆过滤器来避免在大规模数据处理中的重复计算,减少计算开销,并且保证数据的唯一性

    # 判断文章是否已经被处理过
    if BF.EXISTS article_bloom_filter article_id {
        continue; # 跳过本次循环
    }
    
    # 处理文章内容
    process_article(article);
    
    # 将文章 ID 添加到布隆过滤器中
    BF.ADD article_bloom_filter article_id;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3 布隆过滤器在爬虫去重中的应用

    在爬虫系统中为了避免重复爬取已经存在的网页,可以使用 Redis 布隆过滤器来记录已经访问过的 URL

    # 判断 URL 是否已经被访问过
    if BF.EXISTS url_bloom_filter url {
        continue; # 跳过本次循环
    }
    
    # 访问 URL
    response = http.get(url);
    
    # 将 URL 添加到布隆过滤器中
    BF.ADD url_bloom_filter url;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    通过以上的应用案例可以看出Redis 布隆过滤器具有在数据去重、防刷、爬虫去重等方面的广泛应用,并且可以有效地减少重复计算、避免误报或者漏报等问题。

    五、Redis 布隆过滤器的优缺点分析

    1 布隆过滤器的优点

    • 布隆过滤器是一种空间效率高的数据结构可以代替传统的 List, Set 等数据结构,以达到节约内存的目的
    • 布隆过滤器的插入和查询时间复杂度都是 O(k),k 是哈希函数个数,这个值可以根据数据量去调整,因此查询效率非常高。
    • 布隆过滤器可以用于判断一个元素是否在集合中,可以用在缓存穿透的场景中。

    2 布隆过滤器的缺点

    • 布隆过滤器无法删除元素,一旦添加了一个元素就无法删除,因为删除可能会影响其他元素的判断结果
    • 布隆过滤器的误判率是存在的,这是由于多个元素映射到布隆过滤器的同一个位置,使得可能存在“误判”情况
    • 布隆过滤器的哈希函数个数和大小都需要事先确定,这在一些应用场景中可能不太容易确定。

    3 布隆过滤器的适用场景

    • 缓存穿透:如果缓存中不存在某个键对应的值,而且大量的请求又同时访问该键,这时可以通过布隆过滤器过滤掉这些非法请求,降低了对后端系统的压力。
    • 网络爬虫:利用布隆过滤器判断一个链接是否已经被爬取过,避免重复抓取同一链接,提高爬虫的效率。
    • 黑名单过滤:将不良网址、IP 地址等加入到布隆过滤器中,当请求过来时直接使用布隆过滤器判断是否需要过滤掉该请求。
  • 相关阅读:
    JS基础6--逻辑运算符
    虚拟存储器-11.1
    启动 React APP 后经历了哪些过程
    vue-组件通信(二)
    供应商生命周期管理的8个独特阶段
    vue基础 组合式和响应式 && 模板语法 && 计算属性
    考研英语语法(七)
    macOS Sonoma 桌面小工具活学活用!
    肖sir ——自动化讲解
    修炼k8s+flink+hdfs+dlink(四:k8s(二)组件)
  • 原文地址:https://blog.csdn.net/u010349629/article/details/130895765