Redis的BitMap实现分布式布隆过滤器

布隆过滤器(Bloom Filter)是一种高效的概率型数据结构,用于判断一个元素是否属于一个集合。它通过使用哈希函数和位数组来存储和查询数据,具有较快的插入和查询速度,并且占用空间相对较少。

引入依赖

<!--切面-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><!-- 数据库-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version>
</dependency>

properties配置

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/itcast?serverTimezone=GMT%2B8&useUnicode=true&logger=Slf4JLogger&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root123
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.pool-name=HikariCPDatasource
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=180000
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.connection-test-query=SELECT 1
mybatis-plus.configuration.log-impl= org.apache.ibatis.logging.stdout.StdOutImpl
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.timeout=10s
spring.redis.password=123

自定义注解


import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 此注解可作用于控制器方法,或者服务类方法** 使用示例,在目标方法上添加如下注解* <pre>*     BitMap(key = "user", id = "#id")* </pre>**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface BitMap {/*** <p>同一类集合的唯一标识符 商品表、订单表分别设置不同key</p>*/String key();/*** 支持{@code SPEL}表达式* 含义是以被调用方法参数<code>id</code>的值作为主键ID*/String id() default "#id";
}

切面

import com.example.demo.annotation.BitMap;
import com.example.demo.util.ParserUtils;
import com.example.demo.util.RedisBitMapUtils;
import com.example.demo.util.ResponseResult;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.TreeMap;/*** Redis BitMap AOP**/
@Aspect
@Component
public class BitMapAspect {private static final Logger logger = LoggerFactory.getLogger(BitMapAspect.class);@Resourceprivate RedisBitMapUtils redisBitMapUtils;@Pointcut("@annotation(com.example.demo.annotation.BitMap)")public void aspect() {}@Around("aspect()")public Object doAround(ProceedingJoinPoint point) throws Throwable {// 通过 point 对象获取方法签名信息。MethodSignature signature = (MethodSignature) point.getSignature();// 通过方法签名获取当前方法对象。Method method = signature.getMethod();// 获取当前方法上的 BitMap 注解。BitMap annotation = method.getAnnotation(BitMap.class);// 获取方法参数名和参数值的映射关系,并将结果保存到TreeMap中。TreeMap<String, Object> map = ParserUtils.createTreeMap(point, signature);// 从参数映射中获取 id 参数对应的值。String idString = ParserUtils.parse(annotation.id(), map);if (idString != null) {long id = Long.parseLong(idString);if (redisBitMapUtils.isPresent(annotation.key(), id)) {return point.proceed();} else {logger.info(String.format("当前主键ID{%d}不存在", id));return method.getReturnType().equals(ResponseResult.class) ? ResponseResult.okResult() : null;}}throw new RuntimeException("主键ID解析不正确,请按照参考格式书写");}}

RedisBitMap工具类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;/*** {@link RedisBitMapUtils}工具类*/
@Component
public class RedisBitMapUtils {private static final Logger logger = LoggerFactory.getLogger(RedisBitMapUtils.class);@Resourceprivate   StringRedisTemplate stringRedisTemplate ;ValueOperations<String, String> opsForValue;@PostConstructpublic  void init() {opsForValue= stringRedisTemplate.opsForValue();}/*** 该方法可以方便地将一个集合中的每个元素根据给定的映射函数进行转换,* 并返回一个新的列表。如果集合为空或为null,则返回一个空列表。* @param list* @param action* @return* @param <T>* @param <R>*/public  <T, R> List<R> toList(final Collection<T> list, final Function<? super T, ? extends R> action) {Objects.requireNonNull(action);if (Objects.nonNull(list)) {return list.stream().map(action).collect(Collectors.toList());}return Collections.emptyList();}/*** <p>本方法在首次初始化以{@code key}为参数的BitMap时执行</p>* <p>首先删除Key 然后重新构建BitMap</p>** @param <T> 主键类型* @param key 每种业务分别对应不同的Key名称* @param ids 主键ID*/public  <T extends Serializable> void init(String key, Collection<T> ids) {remove(key);setBit(key, ids);}/*** <p>本方法在首次初始化以{@code key}为参数的BitMap时执行</p>* <p>首先删除Key 然后重新构建BitMap</p>** @param key    每种业务分别对应不同的Key名称* @param list   实体类对象集合* @param action 主键列(方法引用表示)* @param <T>    实体类泛型* @param <R>    主键列泛型*/public  <T, R extends Serializable> void init(String key, Collection<T> list, Function<T, R> action) {List<R> ids = toList(list, action);init(key, ids);}/*** 检查当前主键ID在Redis BitMap中是否存在 如果存在则执行函数式回调** @param key 每种业务分别对应不同的Key名称* @param id  主键ID* @return {@code R}实例*/public  <T extends Serializable, R> R ifPresent(String key, T id, Function<T, R> action) {if (getBit(key, id)) {return action.apply(id);}return null;}/*** 检查当前主键ID在Redis BitMap中是否存在 如果存在则执行函数式回调** @param key 每种业务分别对应不同的Key名称* @param id  主键ID* @return {@code R}实例*/public  <T extends Serializable, R> R ifPresent(String key, T id, Supplier<R> supplier) {if (getBit(key, id)) {return supplier.get();}return null;}/*** 检查当前主键ID在Redis BitMap中是否存在 如果存在则返回<code>true</code>** @param key 每种业务分别对应不同的Key名称* @param id  主键ID* @return 如果存在则返回<code>true</code>*/public  <T extends Serializable> boolean isPresent(String key, T id) {return getBit(key, id);}/*** 检查当前主键ID在Redis BitMap中是否存在 如果存在则返回<code>true</code>* 本方法是{@link RedisBitMapUtils#getBit(String, Serializable)}的别名方法 方便对外调用** @param key 每种业务分别对应不同的Key名称* @param id  主键ID* @return 如果存在则返回<code>true</code>*/public  <T extends Serializable> boolean checkId(String key, T id) {return getBit(key, id);}/*** 检查当前主键ID(集合)在Redis BitMap中是否存在 只返回存在的主键ID* 本方法是{@link RedisBitMapUtils#getBit(String, Serializable)}的别名方法 方便对外调用** @param key 每种业务分别对应不同的Key名称* @param ids 主键ID* @return 返回存在的主键ID*/public  <T extends Serializable> List<T> checkIds(String key, Collection<T> ids) {return ids.stream().filter(e -> checkId(key, e)).collect(Collectors.toList());}/*** 向Redis BitMap中保存主键ID** @param key 每种业务分别对应不同的Key名称* @param id  主键ID*/public  <T extends Serializable> void setBit(String key, T id) {ifOffsetValid(Objects.hash(id), e -> opsForValue.setBit(key, e, true));}/*** 向Redis BitMap中批量保存主键ID** @param <T> 主键类型* @param key 每种业务分别对应不同的Key名称* @param ids 主键ID*/public  <T extends Serializable> void setBit(String key, Collection<T> ids) {ids.forEach(id -> ifOffsetValid(Objects.hash(id), e -> opsForValue.setBit(key, e, true)));}/*** 检查当前主键ID在Redis BitMap中是否存在 如果存在则返回<code>true</code>** @param key 每种业务分别对应不同的Key名称* @param id  主键ID* @return 如果存在则返回<code>true</code>*/public  <T extends Serializable> boolean getBit(String key, T id) {return ifOffsetValid(Objects.hash(id), e -> opsForValue.getBit(key, e));}/*** 从Redis BitMap中删除当前主键ID** @param key 每种业务分别对应不同的Key名称* @param id  主键ID*/public  <T extends Serializable> void removeBit(String key, T id) {ifOffsetValid(Objects.hash(id), e -> opsForValue.setBit(key, e, false));}/*** 从Redis BitMap中批量删除主键ID** @param key 每种业务分别对应不同的Key名称* @param ids 主键ID* @param <T> 主键类型*/public  <T extends Serializable> void removeBit(String key, Collection<T> ids) {ids.forEach(id -> ifOffsetValid(Objects.hash(id), e -> opsForValue.setBit(key, e, false)));}/*** 将当前分类下的BitMap Key删除* 清空该Key下所有数据*/public  void remove(String key) {stringRedisTemplate.delete(key);}/*** <p>检查偏移量是否合法</p>* <p>Redis字符串支持字符串最大长度512M,因此支持offset的最大值为(2^32)-1</p>** @param offset 偏移量* @param action 映射规则*/private static <N extends Number> Boolean ifOffsetValid(N offset, Function<N, Boolean> action) {Objects.requireNonNull(action);//如果ID用整型表示 那么正整数范围内所有的ID均有效 最大正整数值为2147483647 约为20亿long max = (1L << 32) - 1;if (offset.intValue() >= 0 && offset.intValue() < Integer.MAX_VALUE) {return action.apply(offset);} else {// 如果偏移量类型为长整型,或者整型范围内的最大值小于0且 offset 的值小于等于 maxif (Integer.MAX_VALUE >= 0 && offset.longValue() <= max) {return action.apply(offset);} else {logger.info(String.format("偏移量{%d}越界[0,%s],本次操作不成功!", offset.longValue(), max));return false;}}}
}

response工具类


import lombok.Data;import java.io.Serializable;
@Data
public class ResponseResult<T> implements Serializable {private Boolean success;private Integer code;private String msg;private T data;public ResponseResult() {this.success=true;this.code = HttpCodeEnum.SUCCESS.getCode();this.msg = HttpCodeEnum.SUCCESS.getMsg();}public ResponseResult(Integer code, T data) {this.code = code;this.data = data;}public ResponseResult(Integer code, String msg, T data) {this.code = code;this.msg = msg;this.data = data;}public ResponseResult(Integer code, String msg) {this.code = code;this.msg = msg;}public static ResponseResult errorResult(int code, String msg) {ResponseResult result = new ResponseResult();return result.error(code, msg);}public static ResponseResult okResult() {ResponseResult result = new ResponseResult();return result;}public static ResponseResult okResult(int code, String msg) {ResponseResult result = new ResponseResult();return result.ok(code, null, msg);}public static ResponseResult setHttpCodeEnum(HttpCodeEnum enums) {return okResult(enums.getCode(), enums.getMsg());}public static <T> ResponseResult<T> error(String message) {return new ResponseResult<T>(HttpCodeEnum.SYSTEM_ERROR.getCode(),  message);}public ResponseResult<?> error(Integer code, String msg) {this.success=false;this.code = code;this.msg = msg;return this;}public ResponseResult<?> ok(Integer code, T data) {this.success=true;this.code = code;this.data = data;return this;}public ResponseResult<?> ok(Integer code, T data, String msg) {this.success=true;this.code = code;this.data = data;this.msg = msg;return this;}public static ResponseResult ok(Object data) {ResponseResult result = new ResponseResult();result.setData(data);return result;}}

controller


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.annotation.BitMap;
import com.example.demo.annotation.PreventRepeatSubmit;
import com.example.demo.mapper.StuMapper;
import com.example.demo.model.ResponseResult;
import com.example.demo.model.Student;
import com.example.demo.util.RedisBitMapUtils;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;@RestController
@RequestMapping("/test")
@Validated
public class TestController {@Resourceprivate StuMapper stuMapper;@Resourceprivate StringRedisTemplate stringRedisTemplate;private static final String BITMAP_STU="bitmap_stu";@Resourceprivate RedisBitMapUtils redisBitMapUtils;@GetMapping("init")public com.example.demo.util.ResponseResult init(){List<Student> studentList = stuMapper.selectList(new QueryWrapper<Student>());redisBitMapUtils.init(BITMAP_STU,studentList,Student::getId);return com.example.demo.util.ResponseResult.okResult();}/*** 编程式*/@GetMapping("selectStu1/{id}")@BitMap(key = BITMAP_STU,id = "#id")public com.example.demo.util.ResponseResult selectStu1(@PathVariable Integer id){return com.example.demo.util.ResponseResult.ok(stuMapper.selectById(id));}/*** 注解式*/@GetMapping("selectStu2/{id}")public com.example.demo.util.ResponseResult selectStu2(@PathVariable Integer id){if (redisBitMapUtils.getBit(BITMAP_STU,id)){return com.example.demo.util.ResponseResult.ok(stuMapper.selectById(id));}return com.example.demo.util.ResponseResult.okResult();}}

测试

初始化biemap数据,从数据库种获取所有id并导入redis的key为bitmap_stu的数据

测试数据库存在的数据id:1,走数据库获取数据 

测试数据库的数据id:200,因为id为200不存在在数据库中,所以没有走数据库,减少了数据库压力 

 注解式也生效

达到了使用redis的bitmap实现分布式的布隆过滤器,过滤掉bitmap不存在的数据 

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

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

相关文章

springboot+html实现密码重置功能

目录 登录注册&#xff1a; 前端&#xff1a; chnangePssword.html 后端&#xff1a; controller: Mapper层&#xff1a; 逻辑&#xff1a; 登录注册&#xff1a; https://blog.csdn.net/m0_67930426/article/details/133849132 前端&#xff1a; 通过点击忘记密码跳转…

力扣刷题 day48:10-18

1.4的幂 给定一个整数&#xff0c;写一个函数来判断它是否是 4 的幂次方。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 整数 n 是 4 的幂次方需满足&#xff1a;存在整数 x 使得 n 4x 方法一&#xff1a;不断除以4 #方法一&#xff1a;不断除…

【APP源码】基于Typecho博客程序开发的博客社区资讯APP源码

全新博客社区资讯APP源码 Typecho后端 一款功能全面&#xff0c;用户交互良好&#xff0c;数据本地缓存&#xff0c;集成邮箱验证&#xff0c;在线投稿&#xff0c;&#xff08;内置Mardown编辑器&#xff09;&#xff0c; 快捷评论的的博客资讯APP。同时兼容H5和微信小程序。 …

热门影视APP系统源码 可二开 后端+app+搭建教程

影视APP源码绿豆二开版 后端app搭建教程都在压缩包里&#xff0c;搭建步骤和绿豆一样 安装宝塔 yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh 安装环境 Nginx 1.20.2 MySQL5.6-5.7 php7.0-7.…

Electron之集成vue+vite开发桌面程序

在electron中集成vue开发桌面程序 使用我们之前创建的electron项目 创建vue 项目 命令行进入electron根目录 执行下面命令 npm create vitelatest vue -- --template vue这样就创建了一个vue项目&#xff0c;文件名是vue&#xff0c;命令行进入vue下&#xff0c;执行下面命…

算水质TDS加温度补偿

先上图&#xff0c;就图里这款水质检测&#xff0c;用树莓派3/4的话&#xff0c;要配个温度检测作为温度校正&#xff0c;以及一个adc 元器件。我选ds18b20和ads1115。 再把模拟数据计算过程放一下&#xff1a; 温度检测元器件在农历钟那里提过&#xff0c;就是同款。此处先测个…

GO 语言的函数??

函数是什么&#xff1f; 学过编程的 xdm 对于函数自然不会陌生&#xff0c;那么函数是什么呢&#xff1f; 函数是一段可以重用的代码块&#xff0c;可以被多次调用&#xff0c;我们可以通过使用函数&#xff0c;提高咱们代码代码的模块化&#xff0c;提高程序的可读性和可维护…

基于nodejs+vue学生论坛设计与实现

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

5.12.webrtc接口调用过程

嗨&#xff0c;大家好&#xff0c;我是李超&#xff0c;在上节课中呢&#xff0c;我向你介绍了外接口的设计以及我们红接口展开之后的样子&#xff0c;对吧&#xff1f;那今天呢&#xff1f;我们再来看看整个接口调用过程。那整个这个调用过程啊&#xff0c;非常的复杂&#xf…

【Machine Learning】01-Supervised learning

01-Supervised learning 1. 机器学习入门1.1 What is Machine Learning?1.2 Supervised learning1.3 Unsupervised learning 2. Supervised learning2.1 单元线性回归模型2.1.1 Linear Regression Model&#xff08;线性回归模型&#xff09;2.1.2 Cost Function&#xff08;代…

nodejs+vue 学生宿舍管理系统设计与实现

可将教师信息、宿管信息、学生信息、楼栋信息等输入到系统中。只有管理员才能录入相关的资料&#xff0c;按照提示&#xff0c;输入相应的资料&#xff0c;而“导入”则可以通过上传档案&#xff0c;导入成功后&#xff0c;相应的寝室就会相应的减少。在录入大楼的时候&#xf…

为什么高精度机器人普遍使用谐波减速器而不是普通减速器?

机器人作为一种能够代替人类完成各种工作的智能设备&#xff0c;已经广泛应用于工业生产、医疗卫生、军事防卫等领域。其中&#xff0c;机器人的关节传动系统是机器人运动的核心&#xff0c;而减速器作为关节传动系统中的重要组成部分部分&#xff0c;对机器人的性能和技术水平…