Redis-布隆过滤器(Bloom Filter)详解

news2024/9/22 1:34:47
文章目录
  • 什么是布隆过滤器
    • 布隆过滤器的优点:
    • 布隆过滤器的缺点:
    • 其他问题
  • 布隆过滤器适合的场景
  • 布隆过滤器原理
    • 数据结构
    • 增加元素
    • 查询元素
    • 删除元素
  • 如何使用布隆过滤器
    • Google开源的Guava自带布隆过滤器
    • Redis实现布隆过滤器
      • Redis中配置布隆过滤器
      • Redis中布隆过滤器指令使用
        • 自定义参数
        • 基本操作
      • Java集成Redis使用布隆过滤器
        • pom中引入redisson依赖:
        • 编写代码测试

什么是布隆过滤器

布隆过滤器(Bloom Filter)是 1970 年由布隆提出的,是一种非常节省空间的概率数据结构,运行速度快,占用内存小,但是有一定的误判率且无法删除元素。它实际上是一个很长的二进制向量和一系列随机映射函数组成,主要用于判断一个元素是否在一个集合中。

通常我们都会遇到判断一个元素是否在某个集合中的业务场景,这个时候我们可能都是采用 HashMap的Put方法或者其他集合将数据保存起来,然后进行比较确定,但是如果元素很多的情况下,采用这种方式就会非常浪费空间,最终达到瓶颈,检索速度也会越来越慢,这时布隆过滤器(Bloom Filter)就应运而生了。

布隆过滤器的优点:

  • 支持海量数据场景下高效判断元素是否存在
  • 布隆过滤器存储空间小,并且节省空间,不存储数据本身,仅存储hash结果取模运算后的位标记
  • 不存储数据本身,比较适合某些保密场景

布隆过滤器的缺点:

  • 不存储数据本身,所以只能添加但不可删除,因为删掉元素会导致误判率增加
  • 由于存在hash碰撞,匹配结果如果是“存在于过滤器中”,实际不一定存在
  • 当容量快满时,hash碰撞的概率变大,插入、查询的错误率也就随之增加了

布隆过滤器中一个元素如果判断结果为存在的时候元素不一定存在,但是判断结果为不存在的时候则一定不存在。因此,布隆过滤器不适合那些对结果必须精准的应用场景。

其他问题

  • 不支持计数,同一个元素可以多次插入,但效果和插入一次相同
  • 由于错误率影响hash函数的数量,当hash函数越多,每次插入、查询需做的hash操作就越多

布隆过滤器适合的场景

  • 区块链中使用布隆过滤器来加快钱包同步;以太坊使用布隆过滤器用于快速查询以太坊区块链的日志
  • 数据库防止穿库,Google Bigtable,HBase 和 Cassandra 以及 Postgresql 使用BloomFilter来减少不存在的行或列的磁盘查找。避免代价高昂的磁盘查找会大大提高数据库查询操作的性能
  • 判断用户是否阅读过某一个视频或者文章,类似抖音,刷过的视频往下滑动不再刷到,可能会导致一定的误判,但不会让用户看到重复的内容
  • 网页爬虫对URL去重,采用布隆过滤器来对已经爬取过的URL进行存储,这样在进行下一次爬取的时候就可以判断出这个URL是否爬取过了
  • 使用布隆过滤器来做黑名单过滤,针对不同的用户是否存入白名单或者黑名单,虽然有一定的误判,但是在一定程度上还是很好的解决问题
  • 缓存击穿场景,一般判断用户是否在缓存中,如果存在则直接返回结果,不存在则查询数据库,如果来一波冷数据,会导致缓存大量击穿,造成雪崩效应,这时候可以用布隆过滤器当缓存的索引,只有在布隆过滤器中,才去查询缓存,如果没查询到则穿透到数据库查询。如果不在布隆过滤器中,则直接返回,会造成一定程度的误判
  • WEB拦截器,如果相同请求则拦截,防止重复被攻击。用户第一次请求,将请求参数放入布隆过滤器中,当第二次请求时,先判断请求参数是否被布隆过滤器命中。可以提高缓存命中率。Squid 网页代理缓存服务器在 cache digests 中就使用了布隆过滤器。Google Chrome浏览器使用了布隆过滤器加速安全浏览服务
  • Google 著名的分布式数据库 Bigtable 使用了布隆过滤器来查找不存在的行或列,以减少磁盘查找的IO次数
  • Squid 网页代理缓存服务器在 cache digests 中使用了也布隆过滤器
  • Venti 文档存储系统也采用布隆过滤器来检测先前存储的数据
  • SPIN 模型检测器也使用布隆过滤器在大规模验证问题时跟踪可达状态空间
  • Google Chrome浏览器使用了布隆过滤器加速安全浏览服务

如果允许误判率的话,可以使用布隆过滤器,只有你想不到的,没有你做不到的。

布隆过滤器原理

数据结构

布隆过滤器是由一个固定大小的二进制向量或者位图(bitmap)和一系列映射函数组成的。

对于长度为 m 的位数组,在初始状态时,它所有位置都被置为0,如下图所示:
在这里插入图片描述
位数组中的每个元素都只占用 1 bit ,并且数组中元素只能是 0 或者 1。这样申请一个 100w 个元素的位数组只占用 1000000Bit / 8 = 125000 Byte = 125000/1024 KB ≈ 122KB 的空间。

增加元素

当一个元素加入布隆过滤器中的时候,会进行如下操作:

  • 使用布隆过滤器中的哈希函数对元素值进行计算,得到哈希值(有几个哈希函数得到几个哈希值)
  • 根据得到的哈希值,在位数组中把对应下标的值置为 1
如下图所示:

在这里插入图片描述
接着再添加一个值 “xinlang”,哈希函数的值是3、5、8,如下图所示:
在这里插入图片描述

这里需要注意的是,5 这个 bit 位由于两个值的哈希函数都返回了这个 bit 位,因此被覆盖了。

查询元素

  • 对给定元素再次进行相同的哈希计算
  • 得到哈希值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值存在布隆过滤器当中,如果存在一个值不为 1,说明该元素不在布隆过滤器中
例如我们查询 “cunzai” 这个值是否存在,哈希函数返回了 1、5、8三个值

如下图所示:
在这里插入图片描述
结果得到三个 1 ,说明 “cunzai” 是有可能存在的。

为什么说是可能存在,而不是一定存在呢?主要分为以下几种情况:

因为映射函数本身就是散列函数,散列函数是会有碰撞的情况发生。

  • 情况1:一个字符串可能是 “chongtu” 经过相同的三个映射函数运算得到的三个点跟 “xinlang” 是一样的,这种情况下我们就说出现了误判
  • 情况2: “chongtu” 经过运算得到三个点位上的 1 是两个不同的变量经过运算后得到的,这也不能证明字符串 “chongtu” 是一定存在的
如下图所示:

在这里插入图片描述

鉴于上面的情况,不同的字符串可能哈希出来的位置相同,这种情况我们可以适当增加位数组大小或者调整哈希函数。

布隆过滤器判定某个元素存在,小概率会误判;布隆过滤器判定某个元素不在,则这个元素一定不在。

删除元素

布隆过滤器对元素的删除,肯定不可以,会出现问题,比如上面添加元素的 bit 位 5 被两个变量的哈希值共同覆盖的情况下,一旦我们删除其中一个值。例如“xinlang”而将其置位 0,那么下次判断另一个值例如“baidu”是否存在的话,会直接返回 false,而实际上我们并没有删除它,这就导致了误判的问题。

如何使用布隆过滤器

Google开源的Guava自带布隆过滤器

首先引入Guava的依赖:

那么,在数据量很大的情况下,效率如何呢?

BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), 5000000);
		for (int i = 0; i < 5000000; i++) {
			bloomFilter.put(i);
		}
		long start = System.nanoTime();
		if (bloomFilter.mightContain(500000)) {
			System.out.println("成功过滤到500000");
		}
		long end = System.nanoTime();
		System.out.println("布隆过滤器消耗时间"+(end - start)/1000000L+"毫秒");
    成功过滤到500000
    布隆过滤器消耗时间0毫秒
    
     
     
    • 1
    • 2

    布隆过滤器消耗时间:0毫秒,有点不敢相信呢,匹配速度是不是很快?

    那么,在数据量很大的情况下,1%的误判率结果如何?

    		BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8),5000000,0.01);
    		List<String> list = new ArrayList<>(5000000);
    		for (int i = 0; i < 5000000; i++) {
    			String uuid = UUID.randomUUID().toString();
    			bloomFilter.put(uuid);
    			list.add(uuid);
    		}
    		int mightContainNumber1= 0;
    		NumberFormat percentFormat =NumberFormat.getPercentInstance();
    		percentFormat.setMaximumFractionDigits(2);
    

    3%的误判率结果如何?

    创建一个最多添加 500 个整数的布隆过滤器,并且可以容忍误判率为百分之一(0.01)

    	public void bool(){
    		BloomFilter<Integer> filter = BloomFilter.create(Funnels.integerFunnel(), 500, 0.01);
    		// 判断指定元素是否存在
    		System.out.println(filter.mightContain(1));
    		System.out.println(filter.mightContain(2));
           // 将元素添加进布隆过滤器
    		filter.put(1);
    		filter.put(2);
    		// 判断指定元素是否存在
    		System.out.println(filter.mightContain(1));
    		System.out.println(filter.mightContain(2));
    	}
    

    从上面的结果可以看出:

    • 如果元素实际存在,那么布隆过滤器一定会判断存在
    • 误判率即fpp在3%左右,随着for循环的次数越大,而且越接近3%,那么如果元素不存在,那么布隆过滤器可能会判断存在

    看源码可知这个3%的fpp是Guava中默认的fpp

      public static <T> BloomFilter<T> create(Funnel<? super T> funnel, long expectedInsertions) {
        return create(funnel, expectedInsertions, 0.03); // FYI, for 3%, we always get 5 hash functions
      }
    
     
     
    • 1
    • 2
    • 3

    如下地址是一个免费的在线布隆过滤器在线计算的网址:
    点击这里

    经过哈希计算次数设置为3次,这个3%的误判率和3次哈希运算需要多大空间位数组呢?
    在这里插入图片描述
    计算得到的结果是984.14KiB,100W的key才占用了0.98M,而如果是10亿呢,计算的结果是960M,这个内存空间是完全可以接受的。

    Guava 提供的布隆过滤器的实现还是很不错的,但是它有一个重大的缺陷就是只能单机使用(另外,容量扩展也不容易),而现在互联网一般都是分布式的场景。为了解决这个问题就需要用到Redis中的布隆过滤器了。

    Redis实现布隆过滤器

    Redis中配置布隆过滤器

    1、点击https://redis.io/modules 找到RedisBloom
    2、点击进去下载RedisBloom-master.zip文件,上传到linux
    3、解压缩刚才的RedisBloom文件

    unzip RedisBloom-master.zip
    cd RedisBloom-master
    
     
     
    • 1
    • 2

    编译安装

    make
    
     
     
    • 1

    make完生成redisbloom.so,拷贝到redis的安装目录。

    cp redisbloom.so /home/www/server/redis
    
     
     
    • 1

    在redis.conf配置文件中加入如RedisBloom的redisbloom.so文件的地址,如果是集群则每个配置文件中都需要加入redisbloom.so文件的地址

    loadmodule /home/www/server/redis/redisbloom.so
    
     
     
    • 1

    保存以后重启redis服务

     redis-server redis.conf --loadmodule /home/www/server/redis/redisbloom.so
    
     
     
    • 1

    上面我们有提到需要重启Redis,在本地和测试环境还可以,但是正式环境能不重启就不需要重启,那这么做可以不重启Redis,使用module load命令执行。

    > MODULE LOAD /home/www/server/redis/redisbloom.so
    > module list
    1) 1) "name"
       2) "bf"
       3) "ver"
       4) (integer) 999999
    
     
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    看到以上数据则说明redisbloom加载成功了,模块名name为"bf",模块版本号ver为999999。

    Redis中布隆过滤器指令使用

    使用布隆过滤器完整指令请到官网查看: 点击这里

    自定义参数
    • bf.reserve {key} {error_rate} {capacity}
    • 使用给定的期望错误率和初始容量创建空的布隆过滤器
    • 参数说明:
      • key:布隆过滤器的key
      • error_rate:期望的错误率(False Positive Rate),该值必须介于0和1之间。该值越小,BloomFilter的内存占用量越大,CPU使用率越高
      • capacity:布隆过滤器的初始容量,即期望添加到布隆过滤器中的元素的个数。当实际添加的元素个数超过该值时,布隆过滤器将进行自动的扩容,该过程会导致性能有所下降,下降的程度是随着元素个数的指数级增长而线性下降
    • 返回值:
      • 成功:OK
      • 其它情况返回相应的异常信息
    基本操作
    • bf.add {key} {item}
    • 添加单个元素
    • 参数说明:
      • key:布隆过滤器的名字
      • item:待插入过滤器的元素
    • 返回值:
      • 元素不存在插入成功:返回1
      • 元素可能已经存在:返回0
      • 其它情况返回相应的异常信息
    • bf.madd {key} {item} [item...]
    • 添加多个元素
    • 参数说明:
      • key:布隆过滤器的名字
      • item:待插入过滤器的元素,可插入多个
    • 返回值:
      • 成功:返回一个数组,数组的每一个元素可能为1或0,当item一定不存在时数组元素值为1,当item可能已经存在时数组元素值为0
      • 其它情况返回相应的异常信息
    • bf.exists{key} {item}
    • 判断单个元素是否存在
    • 参数说明:
      • key:布隆过滤器的名字
      • item:待检查的元素
    • 返回值:
      • 元素一定不存在:0
      • 元素可能存在:1
      • 其它情况返回相应的异常信息
    • bf.mexists{key} {item} [item...]
    • 判断多个元素是否存在
    • 参数说明:
      • key:布隆过滤器的名字
      • item:待检查的元素
    • 返回值:
      • 元素一定不存在:0
      • 元素可能存在:1
      • 其它情况返回相应的异常信息
    • bf.insert{key} [CAPACITY {cap}] [ERROR {ERROR}] [NOCREATE] ITEMS {item…}
    • 向key指定的Bloom中添加多个元素,添加时可以指定大小和错误率,且可以控制在Bloom不存在的时候是否自动创建
    • 参数说明:
      • key:布隆过滤器的名字
      • CAPACITY:如果过滤器已创建,则此参数将被忽略
      • ERROR:如果过滤器已创建,则此参数将被忽略
      • expansion:布隆过滤器会自动创建一个子过滤器,子过滤器的大小是上一个过滤器大小乘以expansion。expansion的默认值是2,也就是说布隆过滤器扩容默认是2倍扩容。
      • NOCREATE:如果设置了该参数,当布隆过滤器不存在时则不会被创建。用于严格区分过滤器的创建和元素插入场景。该参数不能与CAPACITY和ERROR同时设置。
      • NONSCALING:设置此项后,当添加到布隆过滤器中的数据达到初始容量后,不会扩容过滤器,并且会抛出异常((error) ERR non scaling filter is full)。
      • ITEMS:待插入过滤器的元素列表,该参数必传。
    • 返回值:
      • 成功:返回一个数组,数组的每一个元素可能为1或0,当item一定不存在时数组元素值为1,当item可能已经存在时数组元素值为0
      • 其它情况返回相应的异常信息
    127.0.0.1:6379> bf.insert name items  zhangsan1 zhangsan2 zhangsan3
    1) (integer) 1
    2) (integer) 1
    3) (integer) 1
    127.0.0.1:6379> bf.insert name items  zhangsan1 zhangsan2 zhangsan3
    1) (integer) 0
    2) (integer) 0
    3) (integer) 0
    127.0.0.1:6379> bf.insert name capacity  10000 error 0.00001  nocreate  items  zhangsan1 zhangsan2 zhangsan3
    1) (integer) 0
    2) (integer) 0
    3) (integer) 0
    127.0.0.1:6379>
    127.0.0.1:6379> bf.insert name capacity  10000 error 0.00001  nocreate  items  zhangsan4 zhangsan5 zhangsan6
    1) (integer) 1
    2) (integer) 1
    3) (integer) 1
    127.0.0.1:6379>
    
     
     
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • bf.scandump {key} {item}
    • 对布隆过滤器进行增量持久化操作
    • 参数说明:
      • key:布隆过滤器的名字
      • item:首次调用传值0,或者上次调用此命令返回的结果值
    • 返回值:
      • 返回连续的(iter, data)对,直到(0,NULL),表示DUMP完成
      • 其它情况返回相应的异常信息
    • bf.scandump {key} {item}
    • 对布隆过滤器进行增量持久化操作
    • 参数说明:
      • key:布隆过滤器的名字
      • item:首次调用传值0,或者上次调用此命令返回的结果值
    • 返回值:
      • 返回连续的(iter, data)对,直到(0,NULL),表示DUMP完成
      • 其它情况返回相应的异常信息
    • bf.info {key}
    • 返回布隆过滤器的相关信息
    • 参数说明:
      • key:布隆过滤器的名字
    • 返回值:
      • Capacity:预设容量
      • Size:实际占用情况,但如何计算待进一步确认
      • Number of filters:过滤器层数
      • Number of items inserted:已经实际插入的元素数量
      • Expansion rate:子过滤器扩容系数(默认2)
    • bf.debug{key}
    • 查看布隆过滤器的内部详细信息
    • 参数说明:
      • key:布隆过滤器的名字
    • 返回值:
      • size:布隆过滤器中已插入的元素数量
      • 每层BloomFilter的详细信息
        • bytes:占用字节数量
        • bits:占用bit位数量,bits = bytes * 8
        • shashes:该层hash函数数量
        • hashwidth:hash函数宽度
        • capacity:该层容量(第一层为BloomFilter初始化时设置的容量,第2层容量 = 第一层容量 * expansion,以此类推)
        • size:该层中已插入的元素数量(各层size之和等于BloomFilter中已插入的元素数量size)
        • ratio:该层错误率(第一层的错误率 = BloomFilter初始化时设置的错误率 * 0.5,第二层为第一层的0.5倍,以此类推,ratio与expansion无关)
    #创建一个容量为5的布隆过滤器,其key为“name”;
    127.0.0.1:6379> bf.reserve name0.1 5
    OK
    
    编写代码测试
    	public void patchingConsum(ConsumPatchingVO vo) throws ParseException {
    		Config config = new Config();
    		SingleServerConfig singleServerConfig = config.useSingleServer();
    		singleServerConfig.setAddress("redis://127.0.0.1:6379");
    		singleServerConfig.setPassword("123456");
    		RedissonClient redissonClient = Redisson.create(config);
    		RBloomFilter<String> bloom = redissonClient.getBloomFilter("name");
    		// 初始化布隆过滤器;  大小:100000,误判率:0.01
    		bloom.tryInit(100000L, 0.01);
    		// 新增10万条数据
    		for(int i=0;i<100000;i++) {
    			bloom.add("name" + i);
    		}
    		// 判断不存在于布隆过滤器中的元素
    		List<String> notExistList = new ArrayList<>();
    		for(int i=0;i<100000;i++) {
    			String str = "name" + i;
    			boolean notExist = bloom.contains(str);
    			if (notExist) {
    				notExistList.add(str);
    			}
    		}
    		if ($.isNotEmpty(notExistList) && notExistList.size() > 0 ) {
    			System.out.println("误判次数:"+notExistList.size());
    		}
    

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

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

    相关文章

    给Wordpress添加评分功能到评论表单

    今天要 给你的 Wordpress 添加评分功能到评论表单 吗&#xff1f; 评分功能效果图 什么类型的网站需要评分&#xff1f; 资源站教程站其他&#xff0c;我也没想到。。。 但我这个网站&#xff0c;因为是电影类的网站&#xff0c;好像还是有点需要的&#xff0c;所以&#xf…

    完美的用户体验:如何设计一个直观和有效的网站导航?

    APP的顶部导航栏对我们来说很熟悉。导航栏是UI设计中不可或缺的一部分&#xff0c;几乎每个页面都使用导航栏。虽然导航栏看起来很简单&#xff0c;不需要太多精力&#xff0c;但是设计一个与产品需求和客户目标高度匹配的导航栏并不是那么容易的。导航栏的设计标准有很多细节需…

    SpringBoot集成MQTT实现交互服务通信

    引言 本文是springboot集成mqtt的一个实战案例。 gitee代码库地址&#xff1a;源码地址 一、什么是MQTT MQTT&#xff08;Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输协议&#xff09;&#xff0c;是一种基于发布/订阅&#xff08;publish/subscribe&…

    C++ : 移除链表元素/合并两个有序链表题解

    目录 1.移除链表元素 分析 代码 2.合并两个有序链表 分析 代码 1.移除链表元素 分析 像这种移除元素的&#xff0c;加个哨兵位头节点会比较方便&#xff0c;因为旧的头会有被移除的情况&#xff0c;不好控制。这里只需要用cur指向待遍历的节点&#xff0c;prev指向cur的…

    AI大牛Karpathy创办Eureka Labs专注AI+教育

    &#x1f989; AI新闻 &#x1f680; AI大牛Karpathy创办Eureka Labs专注AI教育 摘要&#xff1a;前OpenAI大牛Karpathy离职半年后宣布创办专注AI与教育的公司Eureka Labs&#xff0c;旨在通过生成式AI优化教育体验。公司首个项目LLM101n课程已在GitHub获得高赞&#xff0c;目…

    C++ 继承详解:从基础到深入

    继承是面向对象编程中最强大的功能之一&#xff0c;它不仅促进了代码的重用&#xff0c;还帮助我们构建复杂的系统。在C中&#xff0c;通过继承&#xff0c;我们可以创建一个新的类&#xff08;称为派生类&#xff09;来扩展现有类&#xff08;基类&#xff09;的功能。本文将全…

    基于python的百度资讯爬虫的设计与实现

    研究背景 随着互联网和信息技术的飞速发展&#xff0c;网络已经成为人们获取信息的主要来源之一。特别是搜索引擎&#xff0c;作为信息检索的核心工具&#xff0c;极大地改变了人们获取信息的方式。其中&#xff0c;百度作为中国最受欢迎的搜索引擎之一&#xff0c;其新闻搜索…

    [GXYCTF2019]Ping Ping Ping1

    打开靶机 结合题目名称&#xff0c;考虑是命令注入&#xff0c;试试ls 结果应该就在flag.php。尝试构造命令注入载荷。 cat flag.php 可以看到过滤了空格,用 $IFS$1替换空格 还过滤了flag&#xff0c;我们用字符拼接的方式看能否绕过,ag;cat$IFS$1fla$a.php。注意这里用分号间隔…

    【总结】逻辑运算在Z3中运用+CTF习题

    国际赛IrisCTF在前几天举办&#xff0c;遇到了一道有意思的题目&#xff0c;特来总结。 题目 附件如下&#xff1a;&#x1f4ce;babyrevjohnson.tar 解题过程 关键main函数分析如下&#xff1a; int __fastcall main(int argc, const char **argv, const char**envp){int v4…

    Golang | Leetcode Golang题解之第236题二叉树的最近公共祖先

    题目&#xff1a; 题解&#xff1a; func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {parent : map[int]*TreeNode{}visited : map[int]bool{}var dfs func(*TreeNode)dfs func(r *TreeNode) {if r nil {return}if r.Left ! nil {parent[r.Left.Val] rdfs(r.L…

    用 WireShark 抓住 TCP

    Wireshark 是帮助我们分析网络请求的利器&#xff0c;建议每个同学都装一个。我们先用 Wireshark 抓取一个完整的连接建立、发送数据、断开连接的过程。 简单的介绍一下操作流程。 1、首先打开 Wireshark&#xff0c;在欢迎界面会列出当前机器上的所有网口、虚机网口等可以抓取…

    气膜体育馆进校园:政策支持与市场前景—轻空间

    过去20多年&#xff0c;气膜建筑、场馆相关项目在国内落地众多&#xff0c;展现出强大的市场潜力。2022年8月&#xff0c;《北京晚报》粗略统计&#xff0c;北京全市已建有气膜馆百余座&#xff0c;且数量还在不断增加。这一发展趋势不仅仅体现在北京&#xff0c;全国范围内也都…

    微软GraphRAG +本地模型+Gradio 简单测试笔记

    安装 pip install graphragmkdir -p ./ragtest/input#将文档拷贝至 ./ragtest/input/ 下python -m graphrag.index --init --root ./ragtest修改settings.yaml encoding_model: cl100k_base skip_workflows: [] llm:api_key: ${GRAPHRAG_API_KEY}type: openai_chat # or azu…

    如何通过企业微信会话存档保护企业利益?

    赵总: 张经理&#xff0c;最近行业内频发数据泄露事件&#xff0c;我们的客户资料和内部沟通记录安全吗&#xff1f; 张经理: 赵总&#xff0c;我们已经采取了一系列措施来加强数据安全。特别是针对企业微信的沟通记录&#xff0c;我们最近引入了安企神软件&#xff0c;它能很…

    【BUG】已解决:AttributeError: ‘NoneType‘ object has no attribute ‘split‘

    已解决&#xff1a;AttributeError: ‘NoneType‘ object has no attribute ‘split‘ 英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;…

    通用图形处理器设计GPGPU基础与架构(四)

    一、前言 本文将介绍GPGPU中线程束的调度方案、记分牌方案和线程块的分配与调度方案。 二、线程束调度 在计算机中有很多资源&#xff0c;既可以是虚拟的计算资源&#xff0c;如线程、进程或数据流&#xff0c;也可以是硬件资源&#xff0c;如处理器、网络连接或 ALU 单元。调…

    大数据平台之Kafka

    Apache Kafka 是一个分布式流处理平台&#xff0c;最初由 LinkedIn 开发&#xff0c;并在 2011 年开源成为 Apache 项目。Kafka 主要用于构建实时数据管道和流应用&#xff0c;具有高吞吐量、低延迟、容错性强等特点。以下是对 Kafka 的详细介绍&#xff1a; 核心概念 1. Prod…

    爬虫瑞数5案例:某大学总医院

    声明: 该文章为学习使用,严禁用于商业用途和非法用途,违者后果自负,由此产生的一切后果均与作者无关 一、瑞数简介 瑞数动态安全 Botgate(机器人防火墙)以“动态安全”技术为核心,通过动态封装、动态验证、动态混淆、动态令牌等技术对服务器网页底层代码持续动态变换,…

    【 LCD1602显示屏】使用STC89C51控制1602显示、读写操作时序

    文章目录 LCD1602显示概述&#xff1a;引脚说明控制指令接线 控制思路步骤 代码示例总结对databuffer dataShow;的理解 LCD1602显示 概述&#xff1a; LCD1602&#xff08;Liquid Crystal Display&#xff09;是一种工业字符型液晶&#xff0c;能够同时显示 1602 即 32 字符…

    深入理解Android中的缓存与文件存储目录

    &#x1f31f; 引言 在Android应用开发中&#xff0c;合理管理应用的数据存储至关重要。应用可能需要保存各种类型的数据&#xff0c;从简单的配置信息到多媒体文件&#xff0c;甚至是缓存数据以提高性能和用户体验。Android提供了多个内置目录来满足这些需求&#xff0c;但它…