防缓存穿透利器-隆过滤器(BloomFilter)

2023-10-27

防缓存穿透利器-布隆滤器(BloomFilter)

一、布隆过滤器原理

​ 如果想要判断一个元素是不是在一个集合中存在,一般的想法是将所有元素保存起来,然后再拿着这个元素在集合中一个一个进行比对。但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢。 针对这种需要在大量数据中去判断某一个值是事否存在的情况,1970年由布隆提出了布隆过滤器的概念。布隆过滤器本质是一个位数组,位数组就是数组的每个元素都只占用 1 bit 。每个元素只能是 0 或者 1。这样申请一个 10000 个元素的位数组只占用 10000 / 8 = 1250 字节 的空间。布隆过滤器除了一个位数组,还有 n个哈希函数。

​ 它具体的实现思想是并不将具体的数据存储在数组中,而是通过hash函数对要存储的数据进行三次hash运算,并将hash运算的结果做为位数组的下标,将对应的数组元素修改为1。

​ 例如:我们要将"oracle"、“database”、"filter"存储到布隆过滤器中可以这样做。如下图所示

在这里插入图片描述

​ 从上图中可以看到,我们有三个hash函数(h1()、h2()、h3())和一个位数组,oracle经过三个hash函数,得到第1、4、5位为1,database同理得到2、5、10位1,这样如果我们需要判断oracle是否在此位数组中,则通过hash函数判断位数组的1、4、5位是否均为1,如果均为1,则判断oracle在此位数组中,database同理。这就是布隆过滤器判断元素是否在集合中的原理。

​ 但同学们也会发现,如果我们现在要判断"mysql"是否存在,例如它通过三次hash运算得到的值分别是4,5,10。现在即使你的位数中没有存储“mysql”,布隆过滤器也会判断它存在。这是因为"oracle"、“database”、"filter"算出的hash值已经导致上面的三个位置的值被改为了1,这样就会导致误判。但是可以保证的是,如果布隆过滤器判断一个元素不在一个集合中,那这个元素一定不会再集合中。

​ 产生上面误判的主要原因是hash碰撞导致的。但如果我们将位数组设置的足够大,并且让hash运算执行的次数多一些,这样就会降低误判率。

​ 布隆过率器还有另一个问题就是不能删除。这是因为在位数组上的同一个点有可能有多个输入值的映射,如果删除了会影响布隆过滤器里其他元素的判断结果。

​ 所以我们可以总结出布隆过滤器的优缺点如下:

  • 优点:

    • 所占空间小(并不存储真正的数据),空间效率高

    • 查询时间短

  • 缺点:

    • 元素添加到数组中后,不能被删除

    • 有一定的误判率

二、布隆过滤器使用场景

  • 解决缓存穿透问题,缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中就到数据库中去查,并且出于容错考虑,如果从数据库中查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库中去查询,失去了缓存的意义。在流量大时,可能数据库就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。这时候可以用布隆过滤器当缓存的索引,只有在布隆过滤器中,才去查询缓存,如果没查询到,则再到数据库中查。如果不在布隆器中,则直接返回
  • 在业务场景中可以用来判断用户是否阅读过某些文章或视频,比如抖音或头条,当然会导致一定的误判,但不会让用户看到重复的内容。
  • 黑名单:比如在邮件系统中可以使用布隆器设置黑名单,判断邮件地址是否在黑名单中。也可以设IP黑名单防止网格爬虫等。
  • 垃圾邮件过滤,从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱。

三、实践

3.1 通过 Java 编程手动实现布隆过滤器

package com.heima.rbac.controller;

import java.util.BitSet;

public class MyBloomFilter {

    /**
     * 位数组的大小
     */
    private static final int DEFAULT_SIZE = 2 << 24;
    /**
     * 通过这个数组可以创建 6 个不同的哈希函数
     */
    private static final int[] SEEDS = new int[]{5, 17, 48, 73, 93, 132};

    /**
     * 位数组。数组中的元素只能是 0 或者 1
     */
    private BitSet bits = new BitSet(DEFAULT_SIZE);

    /**
     * 存放包含 hash 函数的类的数组
     */
    private SimpleHash[] func = new SimpleHash[SEEDS.length];

    /**
     * 初始化多个包含 hash 函数的类的数组,每个类中的 hash 函数都不一样
     */
    public MyBloomFilter() {
        // 初始化多个不同的 Hash 函数
        for (int i = 0; i < SEEDS.length; i++) {
            func[i] = new SimpleHash(DEFAULT_SIZE, SEEDS[i]);
        }
    }

    /**
     * 添加元素到位数组
     */
    public void add(Object value) {
        for (SimpleHash f : func) {
            bits.set(f.hash(value), true);
        }
    }

    /**
     * 判断指定元素是否存在于位数组
     */
    public boolean contains(Object value) {
        boolean ret = true;
        for (SimpleHash f : func) {
            ret = ret && bits.get(f.hash(value));
        }
        return ret;
    }

    /**
     * 静态内部类。用于 hash 操作!
     */
    public static class SimpleHash {

        private int cap;
        private int seed;

        public SimpleHash(int cap, int seed) {
            this.cap = cap;
            this.seed = seed;
        }

        /**
         * 计算 hash 值
         */
        public int hash(Object value) {
            int h;
            return (value == null) ? 0 : Math.abs(seed * (cap - 1) & ((h = value.hashCode()) ^ (h >>> 16)));
        }

    }
}

3.2 Redis 中的布隆过滤器

redis 在 4.0 的版本中加入了 module 功能,布隆过滤器可以通过 module 的形式添加到 redis 中,所以使用 redis 4.0 以上的版本可以通过加载 module 来使用 redis 中的布隆过滤器。但是这不是最简单的方式,使用 docker 可以直接在 redis 中体验布隆过滤器。

> docker run -d -p 6379:6379 --name bloomfilter redislabs/rebloom

> docker exec -it bloomfilter redis-cli

redis 布隆过滤器主要就两个命令:

  • bf.add 添加元素到布隆过滤器中:bf.add urls http://www.itcast.com
  • bf.exists 判断某个元素是否在过滤器中:bf.exists urls http://www.itcast.com

上面说过布隆过滤器存在误判的情况,在 redis 中有两个值决定布隆过滤器的准确率:

  • error_rate:允许布隆过滤器的错误率,这个值越低过滤器的位数组的大小越大,占用空间也就越大。
  • initial_size:布隆过滤器可以储存的元素个数,当实际存储的元素个数超过这个值之后,过滤器的准确率会下降。

redis 中有一个命令可以来设置这两个值:

bf.reserve urls 0.01 100

三个参数的含义:

  • 第一个值是过滤器的名字。
  • 第二个值为 error_rate 的值。
  • 第三个值为 initial_size 的值。

使用这个命令要注意一点:执行这个命令之前过滤器的名字应该不存在,如果执行之前就存在会报错:(error) ERR item exists

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

防缓存穿透利器-隆过滤器(BloomFilter) 的相关文章

随机推荐

  • Mysql在Mac终端以及Navicat 的基本操作

    1 进入MySQL 打开终端 输入 usr local MySQL bin mysql u root p 其中 root为数据库用户名 输入密码后 密码输入不会被显示 2 接下来就可以对数据库进行操作了 创建数据库 create datab
  • 【ML&DL】【skimming】The Loss Surfaces of Multilayer Networks

    补了一下Yann LeCun的经典工作The Loss Surfaces of Multilayer Networks 1 论文一览 痛点 文章假设并且陆续证明了这样一些事情 1 对于大网络 large size network 而言 绝大
  • 学生写字灯哪个牌子好?精选学生专用台灯第一品牌

    每个孩子的成长都是父母的心头大事 不管是学习上 身体上都想给予孩子最好的 甚至学习也会买台灯 光源的选择也是很重要的 学生在写字的时候用什么台灯牌子比较好呢 今天就来详细介绍一下 几款护眼的学生台灯 一 南卡护眼台灯L1 参考价 399元
  • 你想要的一眼就知道的【Symbol】

    js的基本数据类型 基本类型 String Boolean Number Symbol Undefind Null 引用类型 Array Object Function Symbol 什么是Symbol 是js语言的一种数据类型 和Stri
  • Bus error (core dumped)问题

    问题描述 项目中有多线程的操作 一个线程运行没有问题 两个线程同时运行时 出现报错 Bus error core dumped 原因分析 问题的原因 指针的中赋值与内容拷贝的问题 业务逻辑中有图像数据的拷贝过程 图像数据是unsinged
  • Mysql之视图、索引【第五篇】

    大纲 一 视图 1 什么是视图 1 MySQL 视图 View 是一种虚拟的表 是从数据库中一个或多个表中导出来的表 视图由列和行构成 行和列的数据来自于定义视图的查询中所使用的表 并且还是在使用视图时动态生成的 2 数据库中存放了视图的定
  • ipfs使用二进制文件部署私有链

    注 此版本仅适用于ipfs go ipfs v0 4 18 版本 IPFS多节点 才能构建一个本地的分布式文件系统 在联盟链开发环境下 多数会使用到IPFS多节点私有网存储文件 一 IPFS二进制安装 1 1 下载ipfs二进制文件 wge
  • Python接口自动化测试之详解post请求

    前言 在HTTP协议中 与get请求把请求参数直接放在url中不同 post请求的请求数据需通过消息主体 request body 中传递 且协议中并没有规定post请求的请求数据必须使用什么样的编码方式 所以其请求数据可以有不同的编码方式
  • wpscloudsvr.exe 怎么删除

    WPS Office安装之后 一直很卡 主要是wpscloudsvr exe这个登录账号的进程太卡了 把它禁止了就行了 1 打开 任务管理器 gt 服务 gt 找到 wpscloudsvr 右键 停止 2 先打开wps 任务管理器 gt 进
  • python 使用 pymssql 调用存储过程并让他返回值

    众所周知 pymssql 库并不支持 暂时 调用存储过程 只能使用原生的sql 语句让其调用 这样一来如果需要让pymssql调用存储过程并让其返回值 显然return语句是不能用了 但是我们可以使用 select 语句让其返回值 比如 我
  • Redis之key和value可以存储的最大值

    文章目录 Redis之key和value可以存储的最大值 1 Redis的key可以存储的最大值 2 Redis的value可以存储的最大值 Redis之key和value可以存储的最大值 1 Redis的key可以存储的最大值 虽然Key
  • ue4文档学习进度

    由于Ue4文档很多 有时又间隔一段时间再看 忘了在哪里了 所以先记录下 截至2022年1月18日 触发器Actor https docs unrealengine com 4 26 zh CN Basics Actors Triggers
  • STM32野火指南者中断EXTI按键点灯

    一 野火官方中断框图 1 输入线 外部中断选择 如 EXTI0 EXTI19 2 配置中断所需寄存器 如 GPIO EXTI NVIC 3 按键1按键2 采用上升沿 下降沿触发 自己设定 二 编程顺序 1 初始化GPIO 即连接到EXTI的
  • [Android学习] 1. 简易登录界面设计

    通过对活动及控件的学习 今天制作一个简易登录界面 简要记录一下操作过程 遇到的问题及学到的经验 希望各位老师多多提出问题不吝赐教 预期设计效果图 设计要求 1 布局不限 参考上图 2 利用EditText制作输入框 有语言提示 3 登录注册
  • 浅谈企业微信公域到私域流量玩法

    一 搭建私域流量池 高效引流客户 客户通过渠道活码添加员工 自动打标签 实时统计引流情况 发送个性化的欢迎语 第一时间送上问候 二 将企业公域流量引流裂变为私域 生成引流裂变海报 通过老用户奖励式分享拉新 实现用户指数级增长 加强用户联系
  • AMD GPU安装运行stable diffusion

    本文操作环境为Windows10 11 AMD AI绘画是一种利用人工智能技术进行绘画的方法 它可以通过机器学习算法来学习艺术家的风格 并生成类似于艺术家的作品 最近 AI绘画技术得到了很大的发展 许多公司和研究机构都在进行相关的研究和开发
  • [C-SAE] SPAT解析消息及说明

    1 消息内容
  • PyTorch 训练一个分类器

    文章目录 0 前言 1 加载和规范化CIFAR10 2 定义一个卷积网络 3 定义损失函数和优化器 4 训练网络 5 测试网络 6 在GPU上训练模型 参考资料 0 前言 TRAINGING A CLASSIFIER 这篇教程很清楚的描述了
  • Visual Studio 2017 许可证已过期解决方案

    参考文章 https blog csdn net qq 19678579 article details 76692822
  • 防缓存穿透利器-隆过滤器(BloomFilter)

    防缓存穿透利器 布隆滤器 BloomFilter 一 布隆过滤器原理 如果想要判断一个元素是不是在一个集合中存在 一般的想法是将所有元素保存起来 然后再拿着这个元素在集合中一个一个进行比对 但是随着集合中元素的增加 我们需要的存储空间越来越