Redis 工具类 与 Redis 布隆过滤器

Redis 工具类

1. 核心依赖

<!--redis-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.1-jre</version>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.31</version>
</dependency>

2. 序列化

public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");private Class<T> clazz;static {ParserConfig.getGlobalInstance().setAutoTypeSupport(true);}public FastJsonRedisSerializer(Class<T> clazz) {super();this.clazz = clazz;}@Overridepublic byte[] serialize(T t) throws SerializationException {if (t == null) {return new byte[0];}return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);}@Overridepublic T deserialize(byte[] bytes) throws SerializationException {if (bytes == null || bytes.length <= 0) {return null;}String str = new String(bytes, DEFAULT_CHARSET);return JSON.parseObject(str, clazz);}protected JavaType getJavaType(Class<?> clazz){return TypeFactory.defaultInstance().constructType(clazz);}
}
@Configuration
public class RedisSerializeConfig {@Bean@SuppressWarnings(value = { "unchecked", "rawtypes" })public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();template.setConnectionFactory(connectionFactory);FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);// 使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}
}

3. 布隆过滤器

/*** 算法过程:* 1. 首先需要k个hash函数,每个函数可以把key散列成为1个整数* 2. 初始化时,需要一个长度为n比特的数组,每个比特位初始化为0* 3. 某个key加入集合时,用k个hash函数计算出k个散列值,并把数组中对应的比特位置为1* 4. 判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,认为在集合中。**/
@Component
public class BloomFilterHelper<T> {private int numHashFunctions;private int bitSize;private Funnel<T> funnel;private static final int NUM_BITS = (int) 1e4;private static final double RATE = 0.03;//不存在误判为存在的概率private static void funnel(@Nullable Object o, PrimitiveSink primitiveSink) {primitiveSink.putBytes(o.toString().getBytes());}public BloomFilterHelper() {this((Funnel) BloomFilterHelper::funnel, NUM_BITS, RATE);}public BloomFilterHelper(Funnel<T> funnel, int expectedInsertions, double fpp) {this.funnel = funnel;// 计算bit数组长度bitSize = optimalNumOfBits(expectedInsertions, fpp);// 计算hash方法执行次数numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, bitSize);}public int[] getHashOffset() {return new int[numHashFunctions];}public int[] murmurHashOffset(T value) {int[] offset = new int[numHashFunctions];long hash64 = Hashing.murmur3_128().hashObject(value, funnel).asLong();int hash1 = (int) hash64;int hash2 = (int) (hash64 >>> 32);for (int i = 1; i <= numHashFunctions; i++) {int nextHash = hash1 + i * hash2;if (nextHash < 0) {nextHash = ~nextHash;}offset[i - 1] = nextHash % bitSize;}return offset;}/*** 计算bit数组长度*/private int optimalNumOfBits(long n, double p) {if (p == 0) {// 设定最小期望长度p = Double.MIN_VALUE;}return (int) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));}/*** 计算hash方法执行次数*/private int optimalNumOfHashFunctions(long n, long m) {return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));}
}
@Component
@RequiredArgsConstructor
public class RedisBloomFilter {private final RedisTemplate redisTemplate;private final BloomFilterHelper bloomFilterHelper;public void init(String bloomFilterName) {int[] offset = bloomFilterHelper.getHashOffset();for (int i : offset) {redisTemplate.opsForValue().setBit(bloomFilterName, i, true);}}/*** 根据给定的布隆过滤器添加值*/public <T> void add(String bloomFilterName, T value) {int[] offset = bloomFilterHelper.murmurHashOffset(value);for (int i : offset) {redisTemplate.opsForValue().setBit(bloomFilterName, i, true);}}/*** 根据给定的布隆过滤器判断值是否存在*/public <T> boolean contains(String bloomFilterName, T value) {int[] offset = bloomFilterHelper.murmurHashOffset(value);for (int i : offset) {if (!redisTemplate.opsForValue().getBit(bloomFilterName, i)) {return false;}}return true;}}

4. Redis工具类

@Component
@RequiredArgsConstructor
@Slf4j
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public class RedisCache {private final RedisTemplate redisTemplate;private final RedisBloomFilter redisBloomFilter;/*** 设置有效时间** @param key Redis键* @param timeout 超时时间* @return true=设置成功;false=设置失败*/public Boolean expire(final String key, final long timeout, final TimeUnit timeUnit) {log.info("为 Redis 的键值设置超时时间\t[{}]-[{}  {}]", key, timeout, timeUnit.name());return redisTemplate.expire(key, timeout, timeUnit);}/*** 原子设置过期时间* @param key* @param value* @param timeout*/public <T> void execute(final String key, final T value, final long timeout, final TimeUnit timeUnit) {log.info("尝试存入 Redis\t[{}]-[{}],超时时间:[{}  {}]", key, value, timeout, timeUnit.name());redisTemplate.execute(new SessionCallback() {@Overridepublic Object execute(RedisOperations redisOperations) throws DataAccessException {redisOperations.multi();redisOperations.opsForValue().set(key, value);redisOperations.expire(key, timeout, timeUnit);return redisOperations.exec();}});}/***  获得对象的剩余存活时间* @param key 键* @return 剩余存活时间*/public long getKeyTTL(final String key, final TimeUnit timeUnit) {int ttl = Math.toIntExact(redisTemplate.opsForValue().getOperations().getExpire(key));String message = null;switch (ttl) {case -1:message = "没有设置过期时间";break;case -2:message = "key不存在";break;default:message = ttl + "  " + TimeUnit.SECONDS.name();break;}log.info("查询 Redis key[{}] 剩余存活时间:{}", key, message);return TimeUnit.SECONDS.convert(ttl, timeUnit);}/*** 缓存基本的对象,Integer、String、实体类等** @param key 缓存的键值* @param value 缓存的值*/public <T> void setCacheObject(final String key, final T value) {log.info("存入 Redis\t[{}]-[{}]", key, value);redisTemplate.opsForValue().set(key, value);}/*** 缓存基本的对象,Integer、String、实体类等** @param key 缓存的键值* @param value 缓存的值* @param timout 超时时间*/public <T> void setCacheObject(final String key, final T value, final long timout, final TimeUnit timeUnit) {log.info("存入 Redis\t[{}]-[{}],超时时间:[{}  {}]", key, value, timout, timeUnit.name());redisTemplate.opsForValue().set(key, value, timout, timeUnit);}/*** 获取键值* @param key 键* @return 键对应的值,并封装成 Optional 对象* @param <T>*/public <T> Optional<T> getCacheObject(final String key) {T value = (T) redisTemplate.opsForValue().get(key);log.info("查询 Redis\t[{}]-[{}]", key, value);return Optional.ofNullable(value);}/*** 让指定 Redis 键值进行自减* @param key 键* @return 自减后的值*/public long decrementCacheNumber(final String key) {long number = redisTemplate.opsForValue().decrement(key);log.info("Redis key[{}] 自减后:{}", key, number);return number;}/*** 让指定 Redis 键值进行自增* @param key 键* @return 自增后的值*/public long incrementCacheNumber(final String key) {long number = redisTemplate.opsForValue().increment(key);log.info("Redis key[{}] 自增后:{}", key, number);return number;}/*** 初始化布隆过滤器* @param bloomFilterName*/public void initBloomFilter(final String bloomFilterName) {log.info("初始化布隆过滤器[{}]", bloomFilterName);redisTemplate.execute(new SessionCallback() {@Overridepublic Object execute(RedisOperations redisOperations) throws DataAccessException {redisOperations.multi();redisBloomFilter.init(bloomFilterName);return redisOperations.exec();}});}/*** 初始化布隆过滤器* @param bloomFilterName* @param timeout* @param timeUnit*/public void initBloomFilter(final String bloomFilterName, final long timeout, final TimeUnit timeUnit) {redisTemplate.execute(new SessionCallback() {@Overridepublic Object execute(RedisOperations redisOperations) throws DataAccessException {redisOperations.multi();redisBloomFilter.init(bloomFilterName);expire(bloomFilterName, timeout, timeUnit);return redisOperations.exec();}});}/*** 加入布隆过滤器* @param bloomFilterName 隆过滤器的名字* @param key key 键*/public <T> void addToBloomFilter(final String bloomFilterName, final T key) {log.info("加入布隆过滤器[{}]\tkey[{}]", bloomFilterName, key);redisTemplate.execute(new SessionCallback() {@Overridepublic Object execute(RedisOperations redisOperations) throws DataAccessException {redisOperations.multi();redisBloomFilter.add(bloomFilterName, key);return redisOperations.exec();}});}/*** 布隆过滤器是否存在该键值* @param bloomFilterName 布隆过滤器的名字* @param key 键* @return 键是否存在*/public <T> boolean containsInBloomFilter(final String bloomFilterName, final T key) {boolean flag = redisBloomFilter.contains(bloomFilterName, key);log.info("key[{}]\t是否存在于布隆过滤器[{}]:\t{}", key, bloomFilterName, flag);return flag;}/*** 缓存Map** @param key* @param data*/public <K, T> void setCacheMap(final String key, final Map<K, T> data) {if (Objects.nonNull(data)) {log.info("Map 存入 Redis\t[{}]-[{}]", key, data);redisTemplate.opsForHash().putAll(key, data);}}/*** 缓存Map** @param key* @param data*/public <K, T> void setCacheMap(final String key, final Map<K, T> data, long timeout, final TimeUnit timeUnit) {if (Objects.nonNull(data)) {Map<String, T> map = new HashMap<>();data.entrySet().stream().parallel().forEach(entry -> {map.put(entry.getKey().toString(), entry.getValue());});log.info("尝试存入 Redis\t[{}]-[{}] 超时时间:[{}  {}]", key, map, timeout, timeUnit.name());redisTemplate.execute(new SessionCallback() {@Overridepublic Object execute(RedisOperations redisOperations) throws DataAccessException {redisOperations.multi();redisTemplate.opsForHash().putAll(key, map);expire(key, timeout, timeUnit);return redisOperations.exec();}});}}/*** 获得缓存的Map** @param key* @return*/public <K, T> Optional<Map<K, T>> getCacheMap(final String key) {Map<K, T> data = redisTemplate.opsForHash().entries(key);data = data.size() == 0 ? null: data;log.info("获取 Redis 中的 Map 缓存\t[{}]-[{}]", key, data);return Optional.ofNullable(data);}/*** 往Hash中存入数据** @param key Redis键* @param hashKey Hash键* @param value 值*/public <K, T> void setCacheMapValue(final String key, final K hashKey, final T value) {log.info("存入 Redis 的某个 Map\t[{}.{}]-[{}]", key, hashKey, value);redisTemplate.opsForHash().put(key, hashKey.toString(), value);}/*** 获取Hash中的数据** @param key Redis键* @param hashKey Hash键* @return Hash中的对象*/public <K, T> Optional<T> getCacheMapValue(final String key, final K hashKey) {T value = (T) redisTemplate.opsForHash().get(key, hashKey.toString());log.info("获取 Redis 中的 Map 的键值\t[{}.{}]-[{}]", key, hashKey, value);return Optional.ofNullable(value);}/*** 删除Hash中的数据** @param key* @param hashKey*/public <K> void delCacheMapValue(final String key, final K hashKey) {log.info("删除 Redis 中的 Map 的键值\tkey[{}.{}]", key, hashKey);redisTemplate.opsForHash().delete(key, hashKey.toString());}/*** 让指定 HashMap 的键值进行自减* @param key HashMap的名字* @param hashKey HashMap的一个键* @return 自减后的值*/public <K> long decrementCacheMapNumber(final String key, final K hashKey) {long number = redisTemplate.opsForHash().increment(key, hashKey.toString(), -1);log.info("Redis key[{}.{}] 自减后:{}", key, hashKey, number);return number;}/*** 让指定 HashMap 的键值进行自增* @param key HashMap的名字* @param hashKey HashMap的一个键* @return 自增后的值*/public <K> long incrementCacheMapNumber(final String key, final K hashKey) {long number = redisTemplate.opsForHash().increment(key, hashKey.toString(), +1);log.info("Redis key[{}.{}] 自增后:{}", key, hashKey, number);return number;}/*** 删除单个对象* @param key*/public boolean deleteObject(final String key) {log.info("删除 Redis 的键值\tkey[{}]", key);return redisTemplate.delete(key);}}

5. 查询Redis与Redis设置缓存的技巧

伪代码:

redisCache.getCacheObject(redisKey).orElseGet(() -> {/* balabala ....*/// 可能查询有误,所以这里也可能没法获取到data(为null),也就可能没必要设置缓存redisCache.setCacheObject(redisKey, data, ttl, unit); /* balabala ....*/return data;}));

现实示例:

在这里插入图片描述

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

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

相关文章

如何修改docker容器的端口映射

要修改 Docker 容器的端口映射&#xff0c;你需要停止并删除现有的容器&#xff0c;然后使用新的端口映射重新运行容器。以下是详细步骤&#xff1a; 停止容器&#xff1a; 使用 docker stop 命令停止正在运行的容器。替换 <container_id> 为你要停止的容器的 ID 或者容器…

Python3零基础教程之Python解释器与开发环境搭建

大家好&#xff0c;我是千与编程&#xff0c;硕士毕业于北京大学&#xff0c;曾先后就职于字节跳动&#xff0c;京东等互联网大厂&#xff0c;目前在编程导航知识星球担任星球嘉宾&#xff0c;著有《AI算法毕设智囊袋》&#xff0c;《保姆级带你通关秋招教程》两大专栏。 今天开…

AndroidStudio 2024-2-21 Win10/11最新安装配置(Ktlion快速构建配置,gradle镜像源)

AndroidStudio 2024 Win10/11最新安装配置 教程目的&#xff1a; (从安装到卸载) &#xff0c;针对Kotlin开发配置&#xff0c;gradle-8.2-src/bin下载慢&#xff0c;以及Kotlin构建慢的解决 好久没玩AS了,下载发现装个AS很麻烦,就觉得有必要出个教程了(就是记录一下:嘻嘻) 因…

Java Web演化史:从Servlet到SpringBoot的技术进程及未来趋势

引言 在快速演进的IT世界里&#xff0c;Java Web开发始终屹立不倒&#xff0c;它不仅承担着历史的厚重&#xff0c;也始终面向未来。 自诞生之日起&#xff0c;Java Web技术就在不断地进化&#xff0c;以适应不同时代的需求。 本文将回顾Java Web开发的重要里程碑&#xff0c;…

Linux环境安装Git(详细图文)

说明 此文档Linux环境为&#xff1a;Ubuntu 22.04&#xff0c;本文档介绍两种安装方式&#xff0c;一种是服务器能联网的情况通过yum或apt命令下载&#xff0c;第二种采用源码方式安装。 一、yum/apt方式安装 1.yum方式安装Git 如果你的服务器环境是centos/redhot&#xff…

js谐音梗创意小游戏《望子成龙》

&#x1f33b; 前言 龙年到来&#xff0c;祥瑞满天。愿您如龙般矫健&#xff0c;事业腾飞&#xff1b;如龙鳞闪耀&#xff0c;生活美满。祝您龙年大吉&#xff0c;万事如意&#xff01; 龙年伊始&#xff0c;我给各位设计了一款原创的小游戏&#xff0c;话不多说&#xff0c;直…

B端系统:工作台页面,如何从平庸走向出众

Hi&#xff0c;大家好&#xff0c;我是贝格前端工场&#xff0c;从事8年前端开发的老司机。大家看过很多平庸的工作台页面&#xff0c;但是仔细分析过平庸的表现吗&#xff0c;仔细思考过如何实现出众的效果吗&#xff1f;这篇文章为你解读。 一、工作台页面是什么&#xff0c;…

小迪安全29WEB 攻防-通用漏洞SQL 注入增删改查盲注延时布尔报错

#知识点&#xff1a; 1、明确查询方式注入 Payload 2、明确查询方式注入产生功能 3、明确 SQL 盲注延时&布尔&报错 #详细点&#xff1a; 盲注就是在注入过程中&#xff0c;获取的数据不能回显至前端页面。 也就是在代码中无echo将sql结果输出出来 此时&#…

css4浮动+清除浮动

浮动 一.常见网页布局1.三种布局方式2.布局准则 二.浮动&#xff08;float&#xff09;1.好处2.概念3.三大特性4.使用5.常见网页布局模板6.注意点 三.清除浮动1.why2.本质3.语法4.四种way&#xff08;后三个都是给父级添加&#xff09;清除浮动总结 一.常见网页布局 1.三种布局…

【elasticsearch实战】知识库文件系统检索工具FSCrawler

需求背景 最近有一个需求需要建设一个知识库文档检索系统&#xff0c;这些知识库物料附件的文档居多&#xff0c;有较多文档格式如&#xff1a;PDF, Open Office, MS Office等&#xff0c;需要将这些格式的文件转化成文本格式&#xff0c;写入elasticsearch 的全文检索索引&am…

http协议工具:apache详解

目录 一、常见的http服务程序 1、 Apache HTTP Server 介绍 1.1 apache 概念 1.2 apache 功能 1.3 apache 特性 2、MPM&#xff08;multi-processing module&#xff09;工作模式 2.1 prefork 2.2 worker 2.3 event 二、Apache HTTP Server安装和相关文件 1、安装方…

WPF 启动项目 Grid、StackPanel 布局

WPF 启动项目 <!--x:Class"WPF_Study.App" 对应类&#xff1a;WPF_Study.App--> <!--xmlns:local"clr-namespace:WPF_Study" 命名空间&#xff1a;WPF_Study--> <Application x:Class"WPF_Study.App"xmlns"http://schema…

Centos stream9 环境使用脚本部署LAMP,实现wordpress

本人将所需要的rpm包都下载完成之后&#xff0c;直接使用脚本的形式安装。 如果需要自己下载rpm包的话&#xff0c;请下载如下包 yum install -y libxml2-devel \ tar \ gcc \ expat-devel \ bzip2-devel \ pcre-devel \ openssl-devel \ perl-devel \ sqlite-devel \ libcur…

CS_Smb_Beacon上线不出网机器

当我们想上线不出网的机器的时候&#xff0c;我们可以通过上传工具来实现&#xff0c;但是有没有不用上传工具的方法呢&#xff1f;&#xff1f;&#xff1f; 有&#xff01;&#xff01;&#xff01; 而且cs会自带&#xff01;&#xff01;&#xff01; 1.基础的网络拓扑 以下…

抽象工厂模式 Abstract Factory

1.模式定义: 提供一个创建一系列相关或互相依赖对象的接口&#xff0c;而无需指定它们具体的类 2. 应用场景: 程序需要处理不同系列的相关产品&#xff0c;但是您不希望它依赖于这些产品的 具体类时&#xff0c; 可以使用抽象工厂 3.优点: 1.可以确信你从工厂得到的产品彼…

nuxt项目搭建

1.先下载nuxt脚手架 yarn create nuxt-app <项目名>&#xff0c;记得安装完项目&#xff0c;npm i,下载node包 目录介绍 components 存放组件分别是头部&#xff08;包含导航&#xff09;和底部 layouts 页面布局&#xff0c;实现一个页面整体架构规则&#xff0c;头…

读书笔记-增强型分析:AI驱动的数据分析、业务决策与案例实践

目录 前言 运用人工智能技术&#xff0c;可以使人类社会变得更美好。人们总是期待产品更适合、服务更贴心、生活更便利。在实践中&#xff0c;技术给企业赋能&#xff0c;企业通过优质的产品和服务满足社会&#xff0c;提升人类福祉。很多金融企业已经开始尝试向潜在客户推送…

【Java系列】JDK 1.8 新特性之 Lambda表达式

目录 1、Lambda表达式介绍2、从匿名类到Lambda转换3、Lambda表达式 六种语法格式语法格式一&#xff1a;无参数、无返回值&#xff0c;只需要一个Lambda体语法格式二&#xff1a;lambda有一个参数、无返回值​语法格式三&#xff1a;Lambda只有一个参数时&#xff0c;可以省略&…

golang通过http访问外部网址

不同项目之前,通过http访问,进行数据沟通 先设定一个接口,确认外部能访问到 PHP写一个接口 public function ceshi_return() {$data $this->request->param();$id $data[id];$res Db::name(user)->field(id,status,price,name)->where([id>$id])->find…

eclipse做NC开发选择nchome后,测试连不通

在bin文件下的config.bat里却又能够连通数据库&#xff0c;在eclipse却不行。 可能是jdk选择的问题&#xff0c;把jdk改成home自带的jdk