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.rhkb.cn/news/262542.html

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

相关文章

leetcode(算法) 83.删除排序链表中的重复元素(python版)

需求 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,1,2] 输出&#xff1a;[1,2] 示例 2&#xff1a; 输入&#xff1a;head [1,1,2,3,3] 输出&…

【Unity3D】ASE制作天空盒

找到官方shader并分析 下载对应资源包找到\DefaultResourcesExtra\Skybox-Cubed.shader找到\CGIncludes\UnityCG.cginc观察变量, 观察tag, 观察代码 需要注意的内容 ASE要处理的内容 核心修改 添加一个Custom Expression节点 code内容为: return DecodeHDR(In0, In1);outp…

jenkins报错:Pseudo-terminal will not be allocated because stdin is not a terminal

jenkins的流水线部分代码如下 sh ssh root192.168.2.234 << remotessh cd /var/lib/jenkins/workspace/txkc /usr/local/maven/apache-maven-3.8.6/bin/mvn clean package -U ls remotessh执行流水线出现报错&#xff1a;Pseudo-terminal will not be allocated because…

【数据结构】排序(1)

目录 一、概念&#xff1a; 二、直接插入排序&#xff1a; 三、希尔排序&#xff1a; 四、直接选择排序&#xff1a; 五、堆排序&#xff1a; 六、冒泡排序&#xff1a; 一、概念&#xff1a; 排序的概念&#xff1a; 使一串记录&#xff0c;按照其中的某个或某些关键字…

【Crypto | CTF】BUUCTF RSA2

天命&#xff1a;密码学越来越难了&#xff0c;看别人笔记都不知道写啥 天命&#xff1a;莫慌&#xff0c;虽然我不会推演法&#xff0c;但我可以用归纳法 虽然我不知道解题的推演&#xff0c;但我可以背公式啊哈哈哈 虽然我不会这题&#xff0c;但是我也能做出来 公式我不知…

百度百科词条在网络推广中的六大作用

也许很多网友都发现了&#xff0c;在网上查资料&#xff0c;百科词条往往是优先展示的。一方面因为百科是搜索引擎自身的平台&#xff0c;另一方面就是因为百科信息权威&#xff0c;网友认可度高。所以企业开展网络营销&#xff0c;百科营销是一块重要阵地。 也有的企业认为百科…

代码检测规范和git提交规范

摘要&#xff1a;之前开发的项目&#xff0c;代码检测和提交规范都是已经配置好的&#xff0c;最近自己新建的项目就记录下相关配置过程。 1. ESlint配置 2013年6月创建开源项目&#xff0c;提供一个插件化的JavaScript代码检测工具&#xff0c;创建项目是生成的eslintrc.js文…

Elasticsearch从入门到精通-01认识Elasticsearch

Elasticsearch从入门到精通-01认识Elasticsearch &#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是程序员行走的鱼 &#x1f342;博主从本篇正式开始ES学习&#xff0c;希望小伙伴可以一起探讨 &#x1f4d6; 本篇主要介绍和大家一块简单认识下ES并了解ES中的主要角色…

装饰模式(Decorator Pattern)

定义 装饰模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许通过将对象包装在装饰器类的实例中来动态地添加新的行为和责任。这种模式可以在不修改现有代码的情况下&#xff0c;灵活地扩展对象的功能。 示例 考虑一个咖啡店的场景&…

springboot集成mqtt

文章目录 前言一、MQTT是什么&#xff1f;二、继承步骤1.安装MQTT2.创建项目&#xff0c;引入依赖3. 对应步骤2的代码3 测试 总结mqtt 启动后访问地址 前言 随着物联网的火热,MQTT的应用逐渐增多 曾经也有幸使用过mqtt,今天正好总结下MQTT的使用; 一、MQTT是什么&#xff1f;…

[OpenAI]继ChatGPT后发布的Sora模型原理与体验通道

前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff1a;https://www.captainbed.cn/z ChatGPT体验地址 文章目录 前言OpenAI体验通道Spacetime Latent Patches 潜变量时空碎片, 建构视觉语言系统…

unity学习(28)——登录功能

有之前注册的知识&#xff0c;登录就很容易处理了。 登陆成功返回id&#xff1a; 登录失败返回null&#xff1a; 测试同一账号不能重复登陆&#xff01;登录成功后最好可以跳到新的场景中 结果是好的&#xff0c;去服务器看一下对应部分的代码&#xff0c;可见&#xff0c;登…

java面向对象上:类的结构之一

目录 1.相同点 2.不同点 2.1 在类中声明的位置的不同 2.2 关于权限修饰符的不同 2.3 默认初始化值的情况&#xff1a; 2.4 在内存中加载的位置 补充&#xff1a;回顾变量的分类&#xff1a; 方式一&#xff1a;按照数据类型&#xff1a; 方式二&#xff1a;按照在类中…

springboot211基于springboot医疗报销系统的设计与实现

医疗报销系统的设计与实现 摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;报销单信息因为其管理内容繁杂&#xff0c;管理数量繁多…

备战蓝桥杯---基础算法刷题1

最近在忙学校官网上的题&#xff0c;就借此记录分享一下有价值的题&#xff1a; 1.注意枚举角度 如果我们就对于不同的k常规的枚举&#xff0c;复杂度直接炸了。 于是我们考虑换一个角度&#xff0c;我们不妨从1开始枚举因子&#xff0c;我们记录下他的倍数的个数sum个&#…

华为算法题 go语言或者ptython

1 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返…

【java面试系列】服务的限流

目录 一、常用的限流算法1.固定窗口计数器(计数器算法)2 滑动窗口计数器算法3. 漏桶算法4 令牌桶算法(`常用`)Google开源项目Guava中的RateLimiter使用的就是令牌桶控制算法二、 分布式限流1、网关层(Nginx、Openresty、Spring Cloud Gateway等)流量限制nginx限流Spring Cl…

C# CAD2016 cass10宗地Xdata数据写入

一、 查看cass10写入信息 C# Cad2016二次开发获取XData信息&#xff08;二&#xff09; 一共有81条数据 XData value: QHDM XData value: 121321 XData value: SOUTH XData value: 300000 XData value: 141121JC10720 XData value: 权利人 XData value: 0702 XData value: YB…

【算法与数据结构】200、695、LeetCode岛屿数量(深搜+广搜) 岛屿的最大面积

文章目录 一、200、岛屿数量1.1 深度优先搜索DFS1.2 广度优先搜索BFS 二、695、岛屿的最大面积2.1 深度优先搜索DFS2.2 广度优先搜索BFS 三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、200、岛屿数量 1.1 深度优先搜…

1.CSS单位总结

CSS 单位总结 经典真题 px 和 em 的区别 CSS 中的哪些单位 首先&#xff0c;在 CSS 中&#xff0c;单位分为两大类&#xff0c;绝对长度单位和相对长度单位。 绝对长度单位 我们先来说这个&#xff0c;绝对长度单位最好理解&#xff0c;和我们现实生活中是一样的。在我们…