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;}));
现实示例:

相关文章:
Redis 工具类 与 Redis 布隆过滤器
Redis 工具类 1. 核心依赖 <!--redis--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency><groupId>com.google.guava…...
自定义el-upload 上传文件
前言 最近在做一个文件上传的功能,后端接口写好了、发现前端上传文件的页面不会写……(我很笨的)然后我就找啊找发现element有个组件是<el-upload/>能直接上传文件。我就想直接用拿来改改改成自己想要的,可是就是这样我花了…...
LeetCode69. x 的平方根(C++)
LeetCode69. x 的平方根 题目链接代码 题目链接 https://leetcode.cn/problems/sqrtx/description/ 代码 class Solution { public:int mySqrt(int x) {int right x, left 0, ans -1;while(left < right){long long mid left (right - left) / 2;if(mid * mid <…...
[c++] 单例模式 + cyberrt TimingWheel 单例分析
单例模式要求一个类在一个进程中只能创建一个对象。比如 cyberrt 中的 TimingWheel 类就是单例模式,这个类管理着一个进程内的所有定时器,只需要一个对象就可以。 单例模式的实现有两种方式,懒汉式和饿汉式。懒汉式,当第一次使用…...
如何在cmd里面创建一个vue项目
在命令提示符(CMD)中创建一个Vue项目,你需要先确保你已经全局安装了Vue CLI(Vue的命令行工具)。如果你还没有安装Vue CLI,可以通过以下命令进行安装: bash复制代码 npm install -g vue/cli # O…...
Day2 JS基础
2.1 运算符 赋值运算符 一元运算符 -- <script>let h20let kh hconsole.log(h) //22console.log(k) //42let i1console.log(i i i) //7 // 递增运算符:var a8aconsole.log(a) //9 var num10var bnumconsole.log(b) //10</script> 比较运…...
mybatis----有用配置知识归纳(狂神说学习总结)
1.mybatis介绍 MyBatis 是一款优秀的持久层框架MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类映射成数据库中的记录 官网 Mybatis中文官方文档 : https…...
【TCP/IP】组播
一、组播介绍 组播(Multicast)是网络技术中数据传输的一种方法,它允许将数据包同时发送给一组指定的目标,而不是单个的目标(单播 Unicast)或所有可能的目标(广播 Broadcast)。组播传…...
java 内存模型
程序计数器 线程私有主要字节码解释器通过读取程序计数器来选取下一条需要执行的指令,比如分支,循环,跳转和异常处理如果执行的是java 方法,那么程序计数器记录的时候虚拟机字节码指令的地址,如果执行的是native 方法…...
Linux——缓冲区封装系统文件操作
📘北尘_:个人主页 🌎个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上,不忘来时的初心 文章目录 一、FILE二、封装系统接口实现文件操作1、text.c2、mystdio.c3、mystdio.h 一、FILE 因为IO相…...
深度学习系列59:文字识别
1. 简单文本: 使用google加的tesseract,效果不错。 首先安装tesseract,在mac直接brew install即可。 python调用代码: import pytesseract from PIL import Image img Image.open(1.png) pytesseract.image_to_string(img, lan…...
学习JAVA的第七天(基础)
目录 static 静态变量 静态方法 工具类: static的注意事项 继承 继承的好处 继承的特点 方法的重写 书写格式 override重写注解 方法重写的要求 this关键字 super关键字 static static表示静态,是Java中的一个修饰符,可以修饰成…...
GoLand 相关
goland 下载依赖 go mod tidy:保持依赖整洁 go mod tidy 命令的作用是清理未使用的依赖,并更新 go.mod 以及 go.sum 文件。 go mod tidy 和 go mod vendor 两个命令是维护项目依赖不可或缺的工具。go mod tidy 确保了项目的 go.mod 文件精简且准确&…...
顶顶通呼叫中心中间件-如何使处于机器人话术中的通话手动转接到坐席分机上
文章目录 前言联系我们实现步骤freeswitch命令转接api接口转接 前言 本文讲解呼叫中心中间件如何手动转接通话。 场景:利用自动外呼进入机器人,在通话过程中,转接到坐席分机上。 联系我们 有意向了解呼叫中心中间件的用户,可以点…...
RabbitMQ开启MQTT协议支持
1)RabbitMQ启用MQTT插件 rootmq:/# rabbitmq-plugins enable rabbitmq_mqtt Enabling plugins on node rabbitmq: rabbitmq_mqtt The following plugins have been configured:rabbitmq_managementrabbitmq_management_agentrabbitmq_mqttrabbitmq_web_dispatch Ap…...
Orange3数据预处理(列选择组件)数据角色及类型描述
在Orange3的文件组件中,datetime、categorical、numeric以及text代表不同种类的数据类型,具体如下: datetime:代表日期和时间类型的数据。通常用于时间序列分析、生存分析和其他需要考虑时间因素的机器学习任务中。例如࿰…...
c sharp资料
资料 c#菜鸟教程 Xml XmlNode 类 XPath或运算 SelectNodes的使用 基础 string.Format 复合格式设置标准数字格式字符串...
《低功耗方法学》翻译——第十四章:电源切换网络设计
第十四章:电源切换网络设计 功率门控是在待机或休眠模式下降低漏电功率最有效的方法,但这种方法存在诸如休眠晶体管占用的硅面积、永久和虚拟电源网络的布线资源以及复杂的功率门控设计和实现过程等开销,影响设计风险和进度。 除了开销外&a…...
如何使用Axure RP制作web页面并实现无公网ip远程访问——“cpolar内网穿透”
文章目录 前言1.在AxureRP中生成HTML文件2.配置IIS服务3.添加防火墙安全策略4.使用cpolar内网穿透实现公网访问4.1 登录cpolar web ui管理界面4.2 启动website隧道4.3 获取公网URL地址4.4. 公网远程访问内网web站点4.5 配置固定二级子域名公网访问内网web站点4.5.1创建一条固定…...
vue2实现无感刷新token
🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录 📘 引言: Ǵ…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
Xcode 16 集成 cocoapods 报错
基于 Xcode 16 新建工程项目,集成 cocoapods 执行 pod init 报错 ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchro…...
