2 Redis的高级数据结构
1、Bitmaps
首先,最经典的应用场景就是用户日活的统计,比如说签到等。
字段串:“dbydc”,根据对应的ASCII表,最后可以得到对应的二进制,如图所示


一个字符占8位(bit),不够就在最高位补 0(零),我们只需设置值为 1 的位。如图所示,二进制最高位是在最左边的,但数组索引最高位是在最右边。字符“d”只需在偏移量(offset,即数组索引)第 1、2、5 位设置 1 ;字符“b”只需在偏移量(offset,即数组索引)第 9、10、14 位设置 1 ;字符“y”只需在偏移量(offset,即数组索引)第 17、18、19、20、23 位设置 1 ;字符“d”只需在偏移量(offset,即数组索引)第 25、26、29 位设置 1 ;字符“c”只需在偏移量(offset,即数组索引)第 33、34、38、39 位设置 1 。

- 字符“d”存储(第 1、2、5 位设置1)
127.0.0.1:6379> setbit mykey 1 1(integer) 0
127.0.0.1:6379> setbit mykey 2 1(integer) 0
127.0.0.1:6379> setbit mykey 5 1(integer) 0
- 字符“b”存储(第 9、10、14 位设置1)
127.0.0.1:6379> setbit mykey 9 1
(integer) 0
127.0.0.1:6379> setbit mykey 10 1
(integer) 0
127.0.0.1:6379> setbit mykey 14 1
(integer) 0
- 字符“y”存储(第 17、18、19、20、23 位设置1)
127.0.0.1:6379> setbit mykey 17 1(integer) 0127.0.0.1:6379> setbit mykey 18 1(integer) 0127.0.0.1:6379> setbit mykey 19 1(integer) 0127.0.0.1:6379> setbit mykey 20 1(integer) 0127.0.0.1:6379> setbit mykey 23 1(integer) 0
- 字符“d”存储(第 25、26、29 位设置1)
127.0.0.1:6379> setbit mykey 25 1(integer) 0127.0.0.1:6379> setbit mykey 26 1(integer) 0127.0.0.1:6379> setbit mykey 29 1(integer) 0
- 字符“c”存储(第 33、34、38、39 位设置1)
127.0.0.1:6379> setbit mykey 33 1(integer) 0127.0.0.1:6379> setbit mykey 34 1(integer) 0127.0.0.1:6379> setbit mykey 38 1(integer) 0127.0.0.1:6379> setbit mykey 39 1(integer) 0
- 获取键 mykey 对应的值
127.0.0.1:6379> get mykey“dbydc”
所以,我们在统计某位用户系统签到的时候,sign=1就是签到,0就是没有签到。
setbit 2023-jack-sign 1 1 //第一日签到
setbit 2023-jack-sign 2 0 //第二日未签到
setbit 2023-jack-sign 3 1 //第三日签到
...
统计出全年的签到次数:
127.0.0.1:6379> BITCOUNT 2023-jack-sign 0 3 #统计1的数量
(integer) 2
2、布隆过滤器与Bitmaps
1970 年布隆提出了一种布隆过滤器的算法,目的是用来判断一个元素是否在一个集合中。
算法由一个二进制数组和一个 Hash 算法组成

布隆过滤器的误判问题?

布隆过滤器的使用场景之缓存穿透
当用户查询的时候,缓存中的key不存在,则进行数据库的大量查询导致的数据库的崩溃场景.

解决思路:在查询的时候快速判断查询的用户是否存在有效的缓存数据,布隆过滤器。

解决缓存穿透的问题,所以在用户查询缓存没有命中的时候,需要兜底去查询数据库,因此redis是无法完全取代数据库的。
3、布隆过滤器的代码实现
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.3</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.nengxing</groupId><artifactId>redis-base</artifactId><version>0.0.1-SNAPSHOT</version><name>redis-base</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/redis.clients/jedis --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.6.3</version></dependency><!--引入Redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-redis</artifactId><version>1.4.2.RELEASE</version></dependency><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.12.3</version></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.1.1-jre</version></dependency><dependency><!-- this is needed or IntelliJ gives junit.jar or junit-platform-launcher:1.3.2 not found errors --><groupId>org.junit.platform</groupId><artifactId>junit-platform-launcher</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
布隆过滤器核心代码
import com.google.common.hash.Funnels;
import com.google.common.hash.Hashing;
import com.google.common.primitives.Longs;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Pipeline;import java.nio.charset.Charset;/*仿Google的布隆过滤器实现,基于redis支持分布式*/
public class RedisBloomFilter {public final static String RS_BF_NS = "rbf:";private int numApproxElements; /*预估元素数量,在配合使用数组的时候使用*/private double fpp; /*布隆过滤器所能接受的最大误差*/private int numHashFunctions; /*自动计算的hash函数个数*/private int bitmapLength; /*自动计算的最优Bitmap长度*/@Autowiredprivate JedisPool jedisPool;/*** 构造布隆过滤器* @param numApproxElements 预估元素数量* @param fpp 可接受的最大误差* @return*/public RedisBloomFilter init(int numApproxElements,double fpp){this.numApproxElements = numApproxElements;this.fpp = fpp;/*位数组的长度*///this.bitmapLength = (int) (-numApproxElements*Math.log(fpp)/(Math.log(2)*Math.log(2)));this.bitmapLength=128;/*算hash函数个数,此处为了简便直接写死*///this.numHashFunctions = Math.max(1, (int) Math.round((double) bitmapLength / numApproxElements * Math.log(2)));this.numHashFunctions=2;return this;}/*** 计算一个元素值哈希后映射到Bitmap的哪些bit上* 用两个hash函数来模拟多个hash函数的情况* * @param element 元素值* @return bit下标的数组*/private long[] getBitIndices(String element){long[] indices = new long[numHashFunctions];/*会把传入的字符串转为一个128位的hash值,并且转化为一个byte数组*/byte[] bytes = Hashing.murmur3_128().hashObject(element, Funnels.stringFunnel(Charset.forName("UTF-8"))).asBytes();long hash1 = Longs.fromBytes(bytes[7],bytes[6],bytes[5],bytes[4],bytes[3],bytes[2],bytes[1],bytes[0]);long hash2 = Longs.fromBytes(bytes[15],bytes[14],bytes[13],bytes[12],bytes[11],bytes[10],bytes[9],bytes[8]);/*用这两个hash值来模拟多个函数产生的值*/long combinedHash = hash1;for(int i=0;i<numHashFunctions;i++){//数组下标indices[i]=(combinedHash&Long.MAX_VALUE) % bitmapLength;combinedHash = combinedHash + hash2;}System.out.print(element+"数组下标");for(long index:indices){System.out.print(index+",");}System.out.println(" ");return indices;}/*** 插入元素** @param key 原始Redis键,会自动加上前缀* @param element 元素值,字符串类型* @param expireSec 过期时间(秒)*/public void insert(String key, String element, int expireSec) {if (key == null || element == null) {throw new RuntimeException("键值均不能为空");}//为了与redis中的其他key进行区别String actualKey = RS_BF_NS.concat(key);try (Jedis jedis = jedisPool.getResource()) {try (Pipeline pipeline = jedis.pipelined()) {for (long index : getBitIndices(element)) {pipeline.setbit(actualKey, index, true);}pipeline.syncAndReturnAll();} catch (Exception ex) {ex.printStackTrace();}jedis.expire(actualKey, expireSec);}}/*** 检查元素在集合中是否(可能)存在** @param key 原始Redis键,会自动加上前缀* @param element 元素值,字符串类型*/public boolean mayExist(String key, String element) {if (key == null || element == null) {throw new RuntimeException("键值均不能为空");}String actualKey = RS_BF_NS.concat(key);boolean result = false;try (Jedis jedis = jedisPool.getResource()) {try (Pipeline pipeline = jedis.pipelined()) {for (long index : getBitIndices(element)) {pipeline.getbit(actualKey, index);}result = !pipeline.syncAndReturnAll().contains(false);} catch (Exception ex) {ex.printStackTrace();}}return result;}@Overridepublic String toString() {return "RedisBloomFilter{" +"numApproxElements=" + numApproxElements +", fpp=" + fpp +", numHashFunctions=" + numHashFunctions +", bitmapLength=" + bitmapLength +'}';}
}
判断不在的,一定不在,判断在的情况,大概率都不在,除非存在一定的hash冲突
测试:
@SpringBootTest
public class TestRedisBloomFilter {private static final int DAY_SEC = 60 * 60 * 24;@Autowiredprivate RedisBloomFilter redisBloomFilter;@Testpublic void testInsert() throws Exception {// System.out.println(redisBloomFilter);redisBloomFilter.insert("bloom:user", "20210001", DAY_SEC);redisBloomFilter.insert("bloom:user", "20210002", DAY_SEC);redisBloomFilter.insert("bloom:user", "20210003", DAY_SEC);redisBloomFilter.insert("bloom:user", "20210004", DAY_SEC);redisBloomFilter.insert("bloom:user", "20210005", DAY_SEC);}@Testpublic void testMayExist() throws Exception {System.out.println(redisBloomFilter.mayExist("bloom:user", "20210001"));System.out.println(redisBloomFilter.mayExist("bloom:user", "20210002"));System.out.println(redisBloomFilter.mayExist("bloom:user", "20210003"));System.out.println(redisBloomFilter.mayExist("bloom:user", "20211001"));}}
Guava的布隆
import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;/*单机下无Redis的布隆过滤器:使用Google的Guava的BloomFilter*/
public class GuavaBF {public static void main(String[] args) {long expectedInsertions = 100000;double fpp = 0.00005;BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), expectedInsertions, fpp);bloomFilter.put("10081");bloomFilter.put("10082");bloomFilter.put("10083");bloomFilter.put("10084");bloomFilter.put("10085");bloomFilter.put("10086");System.out.println("123456:BF--"+bloomFilter.mightContain("123456"));//falseSystem.out.println("10086:BF--"+bloomFilter.mightContain("10086"));//trueSystem.out.println("10084:BF--"+bloomFilter.mightContain("10084"));//true}
}
基于Redisson的实现
import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
/*Redisson底层基于位图实现了一个布隆过滤器,使用非常方便*/
public class RedissonBF {public static void main(String[] args) {Config config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:6379");//构造RedissonRedissonClient redisson = Redisson.create(config);RBloomFilter<String> bloomFilter = redisson.getBloomFilter("phoneList");//初始化布隆过滤器:预计元素为100000000L,误差率为3%bloomFilter.tryInit(100000000L,0.03);//将号码10081~10086插入到布隆过滤器中bloomFilter.add("10081");bloomFilter.add("10082");bloomFilter.add("10083");bloomFilter.add("10084");bloomFilter.add("10085");bloomFilter.add("10086");//判断下面号码是否在布隆过滤器中System.out.println("123456:BF--"+bloomFilter.contains("123456"));//falseSystem.out.println("10086:BF--"+bloomFilter.contains("10086"));//trueSystem.out.println("10084:BF--"+bloomFilter.contains("10084"));//true}
}
布隆过滤器的实现:
Redis + Redisson
Redis + 自主实现
无Redis + Guava
相关文章:
2 Redis的高级数据结构
1、Bitmaps 首先,最经典的应用场景就是用户日活的统计,比如说签到等。 字段串:“dbydc”,根据对应的ASCII表,最后可以得到对应的二进制,如图所示 一个字符占8位(bit),…...
Hive默认分割符、存储格式与数据压缩
目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限(ROW FORMAT)配置标准HQL为: ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...
update_engine-FilesystemVerifierAction和PostinstallRunnerAction
在介绍完了DownloadAction之后,还剩下FilesystemVerifierAction和PostinstallRunnerAction,下面开始对其进行分析。 FilesystemVerifierAction 在数据下载完成后,在DownloadAction中会切换到FilesystemVerifierAction void DownloadAction:…...
深度学习乳腺癌分类 计算机竞赛
文章目录 1 前言2 前言3 数据集3.1 良性样本3.2 病变样本 4 开发环境5 代码实现5.1 实现流程5.2 部分代码实现5.2.1 导入库5.2.2 图像加载5.2.3 标记5.2.4 分组5.2.5 构建模型训练 6 分析指标6.1 精度,召回率和F1度量6.2 混淆矩阵 7 结果和结论8 最后 1 前言 &…...
【Python百宝箱】掌握Python Web开发三剑客:Flask、Django、FastAPI一网打尽
前言 在当今互联网时代,Web应用的开发变得愈发重要和复杂。选择一个合适的Web框架,掌握安全性与认证、数据库与ORM库、前端框架与交互、测试与调试工具等关键知识点,是每个Web开发者都必须面对的挑战。本文将带你深入了解三个流行的Python W…...
【人工智能时代的刑法体系与责任主体概述】
第一节:引言 随着科技的快速发展,人工智能 (Artificial Intelligence, AI) 正日益成为我们生活中不可或缺的一部分。从自动驾驶汽车到语音助手,从智能家居到金融机器人,AI 的广泛应用正不断改变着我们的生活方式和社会结构。然而…...
透视maven打包编译正常,intellj idea编译失败问题的本质
前言 maven多模块类型的项目,在Java的中大型应用中非常常见, 在 module 很多的情况,经常会出现各种各样的编辑依赖错误问题,今天记录一种比较常见的 case : A 子模块依赖 B 子模块,在 Terminal 上终端上 …...
npm报错
npm报错 npm ERR! Fix the upstream dependency conflict, or retry npm ERR! this command with --force or --legacy-peer-deps npm ERR! to accept an incorrect (and potentially broken) dependency resolution. npm ERR! npm ERR! npm ERR! For a full report s…...
【FFmpeg实战】ffmpeg播放器-音视频解码流程
音视频介绍 音视频解码流程 FFmpeg解码的数据结构说明 AVFormatContext:封装格式上下文结构体,全局结构体,保存了视频文件封装格式相关信息AVInputFormat:每种封装格式,对应一个该结构体AVStream[0]:视频文件中每个视频ÿ…...
基于SSM的高校毕业选题管理系统设计与实现
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…...
一个简单的Oracle Redaction实验
本实验包含了: 简单的Oracle Redaction演示针对指定用户的Redaction 实验环境 假设有一个19c多租户数据库,PDB名为orclpdb1。 我们将在orclpdb1中建立2个用户: redact_user: redact管理员schema_user: schema用户 基础实验 首先进入数…...
getchar函数的功能有哪些
getchar函数是C语言标准库中的一个函数,主要用于从标准输入(通常是键盘)获取一个字符。它的功能包括: 从标准输入获取一个字符:getchar函数会等待用户输入一个字符,然后将其返回给程序。可以通过控制台输入…...
信息机房监控系统(动环辅助监控系统)
信息机房监控系统是一个综合性的系统,用于对机房的所有设备及其环境进行集中监控和管理。这种系统主要针对机房的各个子系统进行监控,包括动力系统、环境系统、消防系统、保安系统、网络系统等。 依托电易云-智慧电力物联网,以下是信息机房监…...
最强英文开源模型Llama2架构与技术细节探秘
prerequisite: 最强英文开源模型LLaMA架构探秘,从原理到源码 Llama2 Meta AI于2023年7月19日宣布开源LLaMA模型的二代版本Llama2,并在原来基础上允许免费用于研究和商用。 作为LLaMA的延续和升级,Llama2的训练数据扩充了40%,达到…...
编程刷题网站以及实用型网站推荐
1、牛客网在线编程 牛客网在线编程https://www.nowcoder.com/exam/oj?page1&tab%E8%AF%AD%E6%B3%95%E7%AF%87&topicId220 2、力扣 力扣https://leetcode.cn/problemset/all/ 3、练码 练码https://www.lintcode.com/ 4、PTA | 程序设计类实验辅助教学平台 PTA | 程…...
基于STC12C5A60S2系列1T 8051单片机的SPI总线器件数模芯片TLC5615实现数模转换应用
基于STC12C5A60S2系列1T 8051单片的SPI总线器件数模芯片TLC5615实现数模转换应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍SPI总线器件数模芯片TLC5615介绍通过按…...
【并发编程】Synchronized的使用
📫作者简介:小明java问道之路,2022年度博客之星全国TOP3,专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化,文章内容兼具广度、深度、大厂技术方案,对待技术喜欢推理加验证,就职于…...
【Python】Python基础
文章目录 一、字面值常量和表达式二、变量2.1 定义变量2.2 变量的命名规则2.3 变量的类型2.4 不同类型大小2.5 动态类型 三、注释四、输入与输出五、运算符5.1 算术运算符5.2 关系运算符5.3 逻辑运算符5.4 赋值运算符 一、字面值常量和表达式 print(1 2 * 3) # 7 print(1 2 …...
gitlab环境准备
1.准备环境 gitlab只支持linux系统,本人在虚拟机下使用Ubuntu作为操作系统,gitlab镜像要使用和操作系统版本对应的版本,(ubuntu18.04,gitlab-ce_13.2.3-ce.0_amd64 .deb) book100ask:/$ lsb_release -a No LSB modules are available. Dist…...
Apache Doris (五十四): Doris Join类型 - Bucket Shuffle Join
🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录...
服务器日志分析实战:用Python追踪HTTP 404错误并可视化异常频率
作为一名爬虫开发者或网站运维人员,服务器日志就像飞机的“黑匣子”——它记录了每个请求的来龙去脉。而404错误(页面未找到)尤其值得关注:它可能是用户输错了网址,可能是你爬虫的URL构造逻辑有漏洞,也可能是网站改版后旧的链接失效了。更严重的是,大量突然涌出的404请求…...
Agent开发面试通关攻略:吃透稳拿offer
阅读前置:2026年当下最卷也最缺人的AI岗位,一定是AI Agent开发。最近刷遍CSDN、牛客、力扣最新面经,发现一个非常明显的招聘趋势:普通大模型微调岗位饱和内卷,而AI Agent开发岗位人才严重缺口,薪资更高、竞…...
嘈杂工业场景下的自适应VAD与双码本声纹识别鉴权系统:基于端侧轻量化神经网络与向量量化(VQ)重构
在大型化工车间、能源集控中心以及金融极密隔离库房中,离线声纹识别是物理访问控制和身份安全核验的重要生物特征屏障。然而,在环境本底噪声高达80dB以上的恶劣工业场景下,常规的语音活动检测(VAD)会频繁误触ÿ…...
航空航天为什么离不开高强镁合金?国产替代到哪一步了
飞机每减重一千克,全年大约节省四千两百美元的燃油费用——这是航空工程师熟悉的经验值。在商业航空领域,这个数字还只是财务账;在战斗机、导弹和卫星的世界里,减重的收益被换算成更远的航程、更大的载荷、更高的机动性࿰…...
搞定这 5 个全栈电商项目,面试别再用 Todo-List 凑数了
找独立开发练手项目或者写简历项目时,最忌讳两件事:一是太简单(纯前端 Mock 数据,点两下就没了),二是太假(一上来就硬套微服务、消息队列、高并发,结果自己根本Hold不住)…...
智能体所有权与版权:AI Agent Harness Engineering 创造的作品归谁所有?
1. 标题选项 《AI Agent创作版权迷局破解:从Harness工程原理到所有权划分的完整指南》 《智能体作品归谁?AI Agent Harness Engineering场景下的版权规则深度拆解》 《告别权属纠纷:一文搞懂AI Agent生成内容的所有权、版权与收益分配规则》 《Harness工程视角下的AI创作权:…...
UE5 Mac环境搭好了,然后呢?给新手的第一个5分钟:创建、操控并理解你的第一个角色
UE5 Mac环境搭好了,然后呢?给新手的第一个5分钟:创建、操控并理解你的第一个角色当你第一次打开UE5的Mac版本,面对那个闪烁着光芒的启动界面,内心可能既兴奋又忐忑。安装只是第一步,真正的旅程现在才开始。…...
【DeepSeek集成测试黄金标准】:20年专家亲授5大避坑指南与自动化落地框架
更多请点击: https://intelliparadigm.com 第一章:DeepSeek集成测试黄金标准的演进与核心价值 集成测试在大语言模型工程化落地过程中已从“验证功能可用”跃迁为“保障推理一致性、上下文鲁棒性与安全边界的三位一体质量门禁”。DeepSeek系列模型&…...
【Veo 2提示词SOP白皮书】:从模糊意图到像素级输出的8步标准化工作流(附NASA级测试用例库)
更多请点击: https://intelliparadigm.com 第一章:Veo 2提示词工程的本质与范式跃迁 Veo 2并非单纯升级的视频生成模型,而是一次提示词工程范式的根本性重构——它将传统“指令式提示”(prompt-as-command)转向“意图…...
通过TaotokenCLI工具一键配置开发环境接入参数
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 通过Taotoken CLI工具一键配置开发环境接入参数 对于需要接入多个大模型服务的开发者而言,手动配置每个项目的API密钥、…...
