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 。
在这里插入图片描述

  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
  1. 字符“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
  1. 字符“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
  1. 字符“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
  1. 字符“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
  1. 获取键 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

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

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

相关文章

HTTP1.1升级HTTP2.0

HTTP1.1升级HTTP2.0 一&#xff0c;前言介绍 1.为什么要升级http2.0 HTTP2.0相比于HTTP1.x有以下几个优点&#xff1a; 二进制分帧&#xff1a;HTTP2.0将所有传输的信息分割为更小的消息和帧&#xff0c;并采用二进制格式对它们进行编码&#xff0c;这样可以更好地对数据进行…

【Rust】快速教程——模块mod与跨文件

前言 道尊&#xff1a;没有办法&#xff0c;你的法力已经消失&#xff0c;我的法力所剩无几&#xff0c;除非咱们重新修行&#xff0c;在这个世界里取得更多法力之后&#xff0c;或许有办法下降。——《拔魔》 \;\\\;\\\; 目录 前言跨文件mod多文件mod 跨文件mod //my_mod.rs…

单机版-redis(手动部署)

单机版-redis部署 部署模式:单机版-redis部署 Redis版本&#xff1a;redis-4.0.1 部署redis方式&#xff1a;手动部署 解决GCC问题 linux升级gcc版本详细教程_gcc升级-CSDN博客 在完成第三步时已完成配置&#xff0c;后续为操作命令以及注意事项&#xff1b; 在进行操作数…

【数据结构】——双链表(增删查改)

目录 前言&#xff1a; 一&#xff1a;双链表的定义 ​编辑 二&#xff1a;双向链表的实现 2.1&#xff1a;链表的构造 2.2&#xff1a;创建头节点 2.3&#xff1a;创建节点 2.4&#xff1a;链表的尾插 2.5&#xff1a;链表的打印 2.6&#xff1a;链表的尾删 2.7&a…

[计算机网络]网络层概述

呼,写了这么久终于重新开始啦! 自己落下了太多东西了.....是时候应该重新拾掇起来了. 关于后面的代码项目,我的想法是vilas.js仍然使用js来进行编写,但是后续其他的项目会开始尝试使用ts来进行书写了. 就算是前端也需要点规范吧..... 0.写在前面 这篇文章要和大家道个歉,首…

2023年中职“网络安全“—Linux系统渗透提权③

2023年中职"网络安全"—Linux系统渗透提权③ Linux系统渗透提权任务环境说明&#xff1a;1. 使用渗透机对服务器信息收集&#xff0c;并将服务器中SSH服务端口号作为flag提交&#xff1b;2. 使用渗透机对服务器信息收集&#xff0c;并将服务器中主机名称作为flag提交…

​软考-高级-系统架构设计师教程(清华第2版)【第13章 层次式架构设计理论与实践(P466~495)-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第13章 层次式架构设计理论与实践&#xff08;P466~495&#xff09;-思维导图】 课本里章节里所有蓝色字体的思维导图

Figma 插件学习(一)

一.插件介绍 插件在文件中运行&#xff0c;执行一个或多个用户操作&#xff0c;并允许用户自定义其体验或创建更高效的工作流程。 插件通过专用插件API与Figma的编辑器交互。还可以利用外部Web API。 1.插件API 插件API支持读写功能&#xff0c;允许查看、创建和修改文件的…

打破传统束缚,释放服务潜能:本地生活服务商聚合系统引领行业新风向!

本地生活服务商聚合系统是一种集合多平台、多项目的创新型服务系统&#xff0c;它打破了传统服务商系统的一对一限制&#xff0c;为创业者和运营商带来了诸多优势。小多将深入探讨本地生活服务商聚合系统的优势。 随着互联网的快速发展&#xff0c;本地生活服务也迎来了蓬勃的发…

el-tree 与table表格联动

html部分 <div class"org-left"><el-input v-model"filterText" placeholder"" size"default" /><el-tree ref"treeRef" class"filter-tree" :data"treeData" :props"defaultProp…

彻底删除的文件如何恢复?分享正确方法!

“求救&#xff01;我在清理电脑的过程中&#xff0c;把一些比较久远的文件彻底删除了。但是我突然想起好像有些非常重要的数据也一同被删掉了&#xff0c;这可怎么办&#xff1f;有方法恢复彻底删除的文件么&#xff1f;” 在日常使用电脑的过程中&#xff0c;很多用户或许都会…

机器学习-笔记

绪论 参考期刊 ICCV 偏向视觉CVPR 偏向MLIAAA AI原理ICML 参考链接 CSDN 机器学习知识点全面总结 课堂内容学习-0912-N1 对于特征提取&#xff0c;简而言之就是同类聚得紧&#xff0c;异类分得开&#xff1b;   detection研究的是样本二分类问题&#xff0c;即分为正样本…

Rockdb简介

背景 最近在使用flink的过程中&#xff0c;由于要存储的状态很大&#xff0c;所以使用到了rockdb作为flink的后端存储&#xff0c;本文就来简单看下rockdb的架构设计 Rockdb设计 Rockdb采用了LSM的结构&#xff0c;它和hbase很像&#xff0c;不过严格的说&#xff0c;基于LS…

设计模式-行为型模式-责任链模式

一、什么是责任链模式 责任链模式是一种设计模式。在责任链模式里&#xff0c;很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递&#xff0c;直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求&…

VS2019编译安装GDAL(C++)程序库

一、GDAL简介 GDAL&#xff0c;全称Geospatial Data Abstraction Library&#xff0c;即地理空间数据抽象库&#xff0c;是一个在X/MIT许可协议下读写空间数据的开源库&#xff0c;可以通过命令行工具来进行数据的转换和处理。而在调用中我们常用的OGR&#xff08;OpenGIS Simp…

MATLAB中std函数用法

目录 语法 说明 示例 矩阵列的标准差 三维数组的标准差 指定标准差权重 矩阵行的标准差 数组页的标准差 排除缺失值的标准差 标准差和均值 标准差 std函数的功能是得到标准差。 语法 S std(A) S std(A,w) S std(A,w,"all") S std(A,w,dim) S std(A…

Android---Gradle 构建问题解析

想必做 Android App 开发的对 Gradle 都不太陌生。因为有 Android Studio 的帮助&#xff0c;Android 工程师使用 Gradle 的门槛不算太高&#xff0c;基本的配置都大同小异。只要在 Android Studio 默认生成的 build.gradle 中稍加修改&#xff0c;就都能满足项目要求。但是&am…

面试题c/c++ --STL 算法与数据结构

1.6 STL 模板 模板底层实现&#xff1a;编译器会对函数模板进行两次编译&#xff0c; 在声明的地方对模板代码本身进行编译&#xff0c; 在调用的地方对参数替换后的代码进行编译。 模板传参分析 模板重载 vector 是动态空间&#xff0c; 随着元素的加入&#xff0c; 它的内…

Apache Airflow (十二) :PythonOperator

&#x1f3e1; 个人主页&#xff1a;IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 &#x1f6a9; 私聊博主&#xff1a;加入大数据技术讨论群聊&#xff0c;获取更多大数据资料。 &#x1f514; 博主个人B栈地址&#xff1a;豹哥教你大数据的个人空间-豹…

内网穿透的应用-如何在Docker中部署MinIO服务并结合内网穿透实现公网访问本地管理界面

文章目录 前言1. Docker 部署MinIO2. 本地访问MinIO3. Linux安装Cpolar4. 配置MinIO公网地址5. 远程访问MinIO管理界面6. 固定MinIO公网地址 前言 MinIO是一个开源的对象存储服务器&#xff0c;可以在各种环境中运行&#xff0c;例如本地、Docker容器、Kubernetes集群等。它兼…