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://www.mzph.cn/news/698089.shtml

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

相关文章

ubuntu解决“E: Unable to locate package lrzsz“

今天在ubuntu上安装rzsz包时报错&#xff0c;提示无法定位包&#xff0c;提示如下 出现这个问题是因为apt的源没有更新&#xff0c;我们直接说解决办法 把下面的命令执行一遍即可 sudo add-apt-repository main sudo add-apt-repository universe sudo add-apt-repository re…

fly-barrage 前端弹幕库(1):项目介绍

fly-barrage 是我写的一个前端弹幕库&#xff0c;由于经常在 Bilibili 上看视频&#xff0c;所以对网页的弹幕功能一直蛮感兴趣的&#xff0c;所以做了这个库&#xff0c;可以帮助前端快速的实现弹幕功能。 项目官网地址&#xff1a;https://fly-barrage.netlify.app/&#xff…

Redis信创平替之TongRDS(东方通),麒麟系统安装步骤

我的系统: 银河麒麟桌面系统V10(SP1)兆芯版 1.先进入东方通申请使用 2.客服会发送一个TongRDS包与center.lic给你(我这里只拿到.tar.gz文件,没有网上的什么安装版) 3.上传全部文件到目录中 4.服务节点安装,并启动 tar -zxvf TongRDS-2.2.1.2_P3.Node.tar.gz cd pmemdb/bin/…

QT入门看这一篇就够了——超详细讲解(40000多字详细讲解,涵盖qt大量知识)

目录 一、Qt概述 1.1 什么是Qt 1.2 Qt的发展史 1.3 Qt的优势 1.4 Qt版本 1.5 成功案例 二、创建Qt项目 2.1 使用向导创建 2.2 一个最简单的Qt应用程序 2.2.1 main函数中 2.2.2 类头文件 2.3 .pro文件 2.4 命名规范 2.5 QtCreator常用快捷键 三、Qt按钮小程序 …

MySQL5.7.24解压版安装教程

一、MySQL5.7.24解压版安装步骤 1.在指定目录下解压压缩包。比如在D:\Program Files\mysql下解压 2.在D:\Program Files\mysql\mysql-5.7.24-winx64目录下新建data文件夹&#xff0c;如果此目录下没有my.ini也需要手动创建 3.my.ini 文件配置内容如下 [mysqld] # 设置3306端口…

Nginx 配置前端工程项目二级目录

前提&#xff1a; 前端工程技术框架: vue 后端工程技术工程&#xff1a;spring boot 需求&#xff1a;需要通过二级目录访问前端工程&#xff1a; 如之前&#xff1a;http://127.0.0.1:80/ 改成 http://127.0.0.1/secondDirectory:80/ 一.前端工程支持二级目录 1.编译文…

三、矩阵基本知识

目录 1. 理解 2、矩阵的直观感受 3、矩阵与向量 1. 理解 ①矩阵是一个向量组&#xff0c;由许多 行向量 和 纵向量 组成。 ②矩阵方程求解 用增广矩阵初等变换化为 E 。齐次/非齐次方程组 的解用 初等变化 化为 行最简阶梯型。 ③初步认为由多元一次方程组的系数组成&…

petalinux_zynq7 驱动DAC以及ADC模块之六:qt显示adc波形

前文&#xff1a; petalinux_zynq7 C语言驱动DAC以及ADC模块之一&#xff1a;建立IPhttps://blog.csdn.net/qq_27158179/article/details/136234296petalinux_zynq7 C语言驱动DAC以及ADC模块之二&#xff1a;petalinuxhttps://blog.csdn.net/qq_27158179/article/details/1362…

【C++】——模板初阶 | STL简介

前言: 模板初阶 | STL简介 文章目录 一、模板初阶1.1 函数模板1.2 类模板 二、STL简介 &#xff08;了解&#xff09; 一、模板初阶 泛式编程&#xff08;Generic Programming&#xff09;指的是一种编程范式&#xff0c;其核心思想是编写可以在不同数据类型上通用的代码&#…

智能科技助力服装业:商品计划管理系统的革命性变革

随着智能科技的飞速发展&#xff0c;服装行业正在经历前所未有的变革。在这股浪潮中&#xff0c;商品计划管理系统的智能化转型成为了行业的核心驱动力。这种变革不仅极大地提高了服装企业的运营效率和市场竞争力&#xff0c;更为整个行业的可持续发展注入了新的活力。 智能商…

你听说过柔性数组吗?

目录 1. 柔性数组的概念 2. 柔性数组的特点 3. 柔性数组的使用 4. 柔性数组的优势 5.完结散花 悟已往之不谏&#xff0c;知来者犹可追 创作不易&#xff0c;宝子们&#xff01;如果这篇文章对你们有帮助的话&#…

怎么理解ping?这是我听过最好的回答

晚上好&#xff0c;我是老杨。 Ping这几个字母&#xff0c;已经深入网工人的骨髓了吧&#xff1f; 把Ping用到工作里&#xff0c;肯定不少人在用&#xff0c;但对Ping的了解和理解是不是足够深&#xff0c;取决了你能在工作里用到什么程度&#xff0c;能让它帮你到什么地步。…

美格智能联合罗德与施瓦茨完成5G RedCap模组SRM813Q验证,推动5G轻量化全面商用

全球5G发展进入下半场&#xff0c;5G RedCap以其低成本、低功耗的特性成为行业焦点。近日&#xff0c;中国移动携手合作伙伴率先完成全球最大规模、最全场景、最全产业的RedCap现网规模试验&#xff0c;推动首批芯片、终端具备商用条件&#xff0c;RedCap端到端产业已全面达到商…

10.CSS3的calc函数

CSS3 的 calc 函数 经典真题 CSS 的计算属性知道吗&#xff1f; CSS3 中的 calc 函数 calc 是英文单词 calculate&#xff08;计算&#xff09;的缩写&#xff0c;是 CSS3 的一个新增的功能。 MDN 的解释为可以用在任何长度、数值、时间、角度、频率等处&#xff0c;语法如…

Spring6学习技术|Junit

学习材料 尚硅谷Spring零基础入门到进阶&#xff0c;一套搞定spring6全套视频教程&#xff08;源码级讲解&#xff09; Junit 背景 背景就是每次Test都要重复创建容器&#xff0c;获取对象。就是ApplicationContext和getBean两个语句。通过Spring整合Junit&#xff0c;可以…

sentinel的资源数据指标是如何采集

资源数据采集 之前的NodeSelectorSlot和ClusterBuilderSlot已经完成了对资源调用树的构建, 现在则是要对资源进行收集, 核心点就是这些资源数据是如何统计 LogSlot 作用: 记录异常请求日志, 用于故障排查 public class LogSlot extends AbstractLinkedProcessorSlot<Def…

统计图雷达图绘制方法

统计图雷达图绘制方法 常用的统计图有条形图、柱形图、折线图、曲线图、饼图、环形图、扇形图。 前几类图比较容易绘制&#xff0c;饼图环形图绘制较难。 还有一种雷达图的绘制也较难&#xff0c;今提供雷达图的绘制方法供参考。 本方法采用C语言的最基本功能&#xff1a; &am…

跟着野火学FreeRTOS:第二段(事件组)

在小节里面介绍了二进制信号量&#xff0c;计数信号量&#xff0c;互斥量和递归互斥量等功能&#xff0c;其中二进制信号量和计数信号量&#xff08;也包括队列&#xff09;常用于任务和任务之间以及任务和中断之间的同步&#xff0c;她们具有以下属性&#xff1a; 当等待的事…

Day15-Linux系统特殊权限知识精讲

Day15-Linux系统特殊权限知识精讲 为什么创建文件默认权限为644&#xff0c;目录为755&#xff1f;&#xff1f;&#xff1f; umask 权限掩码。控制系统的文件和目录的默认权限。 [rootoldboy oldboy]# umask 0022针对文件来说&#xff1a;默认权限计算方法 6 6 6 0 2 2 - …

为什么0.1+0.2不等于0.3

一、JS内部的计算是以二进制形式进行的 js里整数和小数转为二进制形式的方法是不一样的&#xff1a; 二、Number类型使用IEEE754标准64位存储 双精度浮点数&#xff08;double类型&#xff09;为每个数分配64位空间&#xff0c;并以科学计数法的方式存储&#xff1a; 那么对于…