高并发下判重难?架构必备技能 - 布隆过滤器

news2024/9/21 6:04:51

系列文章目录

当Dubbo遇到高并发:探究流量控制解决方案
主从选举机制,架构高可用性的不二选择


高并发下判重难?架构必备技能 - 布隆过滤器

  • 系列文章目录
  • 前言
  • 一、布隆过滤器简介
  • 二、特性与应用场景
  • 三、参数定制
  • 四、java版本的Demo
  • 五、总结


前言

相信熟悉高并发架构的同学,一定都接触过一个名词————“布隆过滤器”,又或者一些朋友接触其实是在学习Redis的时候,了解到其中有这么一种数据类型。但实际上,除了Redis,在高并发或者各种存储性质的架构中,你经常能见到这种设计的存在,那么今天我们就好好说一说这个布隆过滤器

📕作者简介:战斧,从事金融IT行业,有着多年一线开发、架构及管理经验;爱好广泛,乐于分享,致力于创作更多高质量内容
📗本文收录于 JAVA架构,有需要者,可直接订阅专栏实时获取更新
📘高质量专栏 RabbitMQ、Spring全家桶 等仍在更新,欢迎指导
📙Zookeeper Redis kafka docker netty等诸多框架,以及架构与分布式专题即将上线,敬请期待


一、布隆过滤器简介

布隆过滤器是1970年由布隆(Burton Howard Bloom)提出的概率型数据结构:它通过位数组和哈希函数的巧妙结合它可以快速地判断一个元素是否属于某个集合,而且具有高效的存储和查询。虽然布隆过滤器是概率性的,但其性能却相当出色,尤其在处理大规模数据集时,能够显著减少资源消耗。

如下图:我们预先准备一块bit空间存放位数组,(所谓位数组就是一个数组,数组每个位置就是一个bit大小,只有0和1两种情况)。并定义两种哈希算法H1、H2。然后分别计算Orange 和 Lemon 这两个单词,显而易见的,我们将得到4个值,找到这4个值在bit空间的bit位,将其置为1。这就代表我们存入了Orange 和 Lemon 这两个单词。
必须清楚的一点是:布隆过滤器并不存储数据原文,只存数据经过哈希计算后的得到的位置,所以严格来说它能判重,但并不算容器。当下次再存Orange 或 Lemon的时候,执行上述步骤,我们就能发现对应bit位全为1,则代表数据可能存储过了
在这里插入图片描述
之所以说可能,是因为可能有别的单词,经过上述H1、H2计算后,落在同样的的两个bit位上,而恰好此时两个bit位都已经被置为1,从而导致误判(如上图的Onion单词)


二、特性与应用场景

其实从上面举得例子,不难总结出布隆过滤器至少有这么几个特性:

  • 快速查询:布隆过滤器具有非常快速的查询操作。无论数据规模多大,查询的时间复杂度都是O(k),其中k是哈希函数的数量

  • 节省空间:相对于其他数据结构,布隆过滤器在存储大规模数据时非常节省空间。它使用位数组来存储数据,而不需要存储实际的元素本身。

  • 概率性判断:布隆过滤器是一个概率性数据结构,意味着在判断一个元素是否存在时,有一定的误判率。如果判断元素不存在,那么它一定不存在;但如果判断元素存在,那么它可能并不存在,因为存在哈希冲突。

  • 元素插入不可逆:一旦将元素插入布隆过滤器,就无法删除或修改该元素。因为删除元素需要将对应的位数组位置置为0,这可能会影响其他元素的判断。

  • 哈希函数的重要性:布隆过滤器的性能和效果与使用的哈希函数密切相关。合理选择和设计哈希函数可以有效降低误判率

结合上述特性,其实我们可以看到布隆过滤器 最适合的场景就是在大数据量下的判重, 因为其速度快、节约空间。虽然可能有“假阳性”(即不存在的数据也判重了)、bit位不可逆的问题,但这些问题通过哈希函数的选择,定好初始bit空间的大小,能很大的程度上的缓解。因此,至今,高并发或重存储的架构下,布隆过滤器,都有着广阔的应用。


三、参数定制

布隆过滤器的大小和哈希函数的个数是根据实际应用场景来确定的,需要平衡误判率和存储空间之间的关系。

一般来说,布隆过滤器的大小是根据预期要处理的数据量和误判率来确定的。误判率越小,需要的空间就越大。哈希函数的个数一般根据预期要处理的数据量和布隆过滤器的大小来确定,一般情况下,哈希函数的个数越多,误判率越小

所以,使用布隆过滤器,有几个方面是我们一开始就要敲定的,就是对未来数据量的预估,以及误差率的限制。我们可以直接给出以下两个公式:

  • m = - (n * ln(p)) / (ln(2)^2)

  • k = (m / n) * ln(2)

其中,m 表示布隆过滤器的大小,k 表示哈希函数的个数,n 表示要处理的数据量,p 表示允许的误差率。我们假设有如下场景:

我们要建立一个网盘产品,为避免一个文件重复存储,预计文件数量将达到20亿条数据,要求在用户上传文件时进行文件判重,误差率要求3%以内。

利用上面的公式:可以计算出:
m = - (20亿 * ln(0.03)) / (ln(2)^2) ≈ 14596881675.7 bits ≈ 1740 MB
k = ( 14596881675.7 / 20亿) * ln(2) ≈ 5.05

因此,可以将布隆过滤器的大小设置为 1740 MB,哈希函数的个数设置为 5个
如果你懒得计算,这里也有一个现成的网站来帮你算: 布隆过滤器参数计算网站 ,可以看到结果是一致的。
在这里插入图片描述


四、java版本的Demo

我们可以利用JAVA中自带的hashCode函数,和BitSet类来写一个布隆过滤器的Demo

public class BloomFilter<T> {
    private final int size;
    private final BitSet bitSet;

    public BloomFilter(int size) {
        this.size = size;
        this.bitSet = new BitSet(size);
    }

    private int hashFunction1(T value) {
        return Math.abs(value.hashCode()) % size;
    }

    private int hashFunction2(T value) {
        return Math.abs(value.hashCode() / 31) % size;
    }

    public void add(T value) {
        int hash1 = hashFunction1(value);
        int hash2 = hashFunction2(value);
        bitSet.set(hash1);
        bitSet.set(hash2);
    }

    public boolean contains(T value) {
        int hash1 = hashFunction1(value);
        int hash2 = hashFunction2(value);
        return bitSet.get(hash1) && bitSet.get(hash2);
    }
}

再写一个main函数进行验证,结果符合预期。

public static void main(String[] args) {
        BloomFilter<String> bloomFilter = new BloomFilter<>(1000);

        bloomFilter.add("apple");
        bloomFilter.add("banana");

        System.out.println(bloomFilter.contains("apple"));  // Output: true
        System.out.println(bloomFilter.contains("orange")); // Output: false
}

在这里插入图片描述


五、总结

虽然,我们在第四章使用java写出了一个布隆过滤器的Demo,但显然是不够好的,比如其设计容量得太小,还使用了两个关联的哈希函数。在真实环境中,如果要我们写过滤器,我们还是要严格遵守第三章的公式进行容量和哈希函数的计算。同时哈希函数也应该具有以下几个特点:

  1. 哈希函数需要快速计算,因为布隆过滤器的效率很大程度上依赖于哈希函数的效率
  2. 哈希函数需要输出的哈希值应该尽可能分散、均匀,并且无法推断出输入元素的信息,这样可以使得误判率最小
  3. 哈希函数互相之间应该独立,这样可以保证哈希值之间相互独立,降低误判率

当然,业界也有现成的布隆过滤器供我们使用,比如Google Guava BloomFilter 、Apache Commons Collections BloomFilter 还有很多人熟知的Redis BloomFilter。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/815702.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

七个步骤, 编写一个 Servlet 的 HelloWorld 程序

目录 1.创建项目 2.引入依赖 ①直接粘贴仓库目标地址代码 ② 下载jar包, 然后导入jar包 3.创建目录 4.编写代码 5.打包代码 6. 部署程序 ①使用IDEA打成war包 ②更方便的部署方式 7.验证程序 1.创建项目 使用IDEA创建一个maven项目 2.引入依赖 引入依赖的方式两种…

selenium-web自动化测试

一、selenium环境部署 1.准备chrome浏览器&#xff08;其他浏览器也行&#xff09; 2.准备chrome驱动包 步骤一&#xff1a;查看自己的谷歌浏览器版本(浏览器版本和驱动版本一定要对应) 步骤二&#xff1a;下载对应的驱动包, 下载路径 : ChromeDriver - WebDriver for Chrom…

环境搭建的上手指南

前言 测试环境是QA开展测试工作的前置条件。稳定和可控的测试环境&#xff0c;可以使测试人员在执行测试用例时无需花费额外的时间去维护。 有些公司运维或者研发部门会帮忙准备好测试环境&#xff0c;但是QA如果一味依赖其他部门&#xff0c;会局限测试工作的开展。 一、什…

OSPF随记

邻居状态机&#xff1a;进度条 工作原理&#xff1a; 1.发送hello报文&#xff0c;建立邻居关系 down&#xff1a;没有发送报文之前 init:开始发送hello报文&#xff0c;自己收到的hello报文中没有自己的route-id 2way:收到的hello报文中有自己的route-id&#xff08;在2w…

selenium如何打开浏览器,等待用户输入完成后,再运行

selenium如何打开浏览器&#xff0c;等待用户输入完成后&#xff0c;再运行 一、在脚本中&#xff0c;等待用户输入 在使用 Selenium 打开浏览器后等待用户输入完成&#xff0c;可以使用 Python 编写一个简单的脚本来实现。首先&#xff0c;确保你已经安装了 Selenium 和对应的…

这 4 个系统可靠性评估指标,可能比 MTTR 更靠谱!

如果要评选研发效能管理中最重要的 10 个度量指标&#xff0c;相信 MTTR&#xff08;Mean Time to Recover&#xff0c;平均恢复时间&#xff09;一定榜上有名。 MTTR 代表一定周期内可修复系统不可用状态的平均持续时长&#xff0c;可以帮助企业更好地理解技术团队与研发工作…

GPU显卡驱动安装

查看GPU版本 lspci | grep -i nvidia从下面的网址ThePCI ID Repository中输入ID查看对应的GPU版本 官网NVIDIA下载对应的驱动 安装下载的文件 sudo sh ./NVIDIA-Linux-x86_64-535.86.05.run 检验是否安装成功 nvidia-smi

机器学习-Regression

机器学习(Regression:Case Study) 前言&#xff1a; 学习资料 videopptblog Example Application 建立一个model&#xff0c;将宝可梦的一些数据作为输入&#xff0c;然后输出宝可梦进化以后的战斗力CP值&#xff0c;这个model的建立尤为重要&#xff0c;但是这个模型的建立…

目标检测中 anchor base和anchor free

目标检测中两种不同anchor的生成 趋势&#xff1a;anchor free越来越受到实时性检测的青睐&#xff0c;&#xff0c;&#xff0c;

redis 高级篇 redis 源码的读取分析

一 redis源码分析 1.1 源码分析 1每一个kv键值对应有一个dictEntry。 2.底层数据结构

《QDebug 2023年7月》

一、Qt Widgets 问题交流 1.QPainter旋转角度绘制线条的一点问题 QPainter 旋转角度&#xff0c;等距绘制若干线条&#xff0c;会出现绘制不均匀的情况&#xff1a; 但是在测试 QML Canvas 绘制时&#xff0c;发现效果是正常的&#xff0c;原来是因为 Canvas 默认的 capStyle…

正则表达式的应用及示例解析

正则表达式&#xff08;Regular Expression&#xff0c;简称Regex&#xff09;是由特殊字符组成的模式字符串&#xff0c;用于匹配和搜索文本中的特定模式。它在数据处理、文本搜索和替换等方面广泛应用。本文将介绍正则表达式的基本语法&#xff0c;并提供常见的正则表达式示例…

Linux系统增加新用户

使用adduser命令&#xff0c;不使用useradd命令。 登录root用户&#xff0c;或者有sudo权限的用户创建。 sudo adduser test修改文件目录的权限 sudo chmod -R 777 /opt/登录新创建的用户创建文件 mkdir test删除用户 userdel -r test增加网络映射&#xff0c;安装samba sudo…

RBAC三级树状菜单实现(从前端到后端)未完待续

1、表格设计 RBAC 2、前端路由 根据不同的用户id显示不同的菜单。 根据路由 3、多级菜单 展示所有权限&#xff0c;并且根据当前用户id展示它所属的角色的所有菜单。 前端树状展示 思路&#xff1a; 后端&#xff1a;传给前端map&#xff0c;map里1个是所有菜单&am…

汽车过户时,怎么选到理想的好车牌?

在汽车过户的过程中&#xff0c;选到一副理想的好车牌就像买彩票中大奖一样令人兴奋。但是&#xff0c;怎样找到这样一块车牌呢&#xff1f;这就是本文要探讨的问题。 首先&#xff0c;我们来聊聊选车牌的技巧。很多人喜欢选择有特别数字的车牌&#xff0c;如“8888”、“6666”…

太狠了,Spring 全家桶笔记, 一站式通关全攻略, 已入职某厂涨薪 18K

Spring 早已成为 Java 后端开发事实上的行业标准&#xff0c;无数的公司选择 Spring 作为基础的开发框架&#xff0c;大部分 Java 后端程序员在日常工作中也会接触到 Spring &#xff0c;因此&#xff0c;如何用好 Spring &#xff0c;也就成为 Java 程序员的必修课之一。 为了…

解决方案之执行gradle报错--org.codehaus.groovy.runtime.InvokerHelper

这里写自定义目录标题 报错解决方案 报错 在执行./gradlew clean时报错如下&#xff1a; FAILURE: Build failed with an exception.* What went wrong: Could not initialize class org.codehaus.groovy.runtime.InvokerHelper > Exception java.lang.NoClassDefFoundErr…

自动callback

using UnityEngine;public class AsyncCallbackScript : MonoBehaviour {public delegate void fun(string msg);void Start(){fun test AAA;//test.BeginInvoke("天王盖地虎", asyncCallback > BBB(), null);test.BeginInvoke("天王盖地虎 宝塔镇河妖"…

Java实现简单小画板

Java制作简单画板&#xff0c;包括两个类&#xff0c;一个主要画板类Drawpad&#xff0c;一个画板监听器DrawListener类。 1、Drawpad类&#xff0c;包括画板&#xff0c;画板功能设计&#xff0c;保存图片等 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 2…

Boost开发指南-3.6weak_ptr

weak_ptr weak_ptr是为配合shared_ptr而引入的一种智能指针&#xff0c;它更像是shared_ptr的一个助手而不是智能指针&#xff0c;因为它不具有普通指针的行为&#xff0c;没有重载 operator*和->。它的最大作用在于协助shared_ptr工作&#xff0c;像旁观者那样观测资源的使…