spring boot 集成 RedisSearch 和 RedisJSON
1. 准备工作
-
环境说明
- java 8;redis7.2.2,redis集成RedisSearch、redisJson 模块;spring boot 2.5
- 在执行 redis 命令, 或者监控 程序执行的redis 指令时,可以采用 redisinsight查看,下载地址。
-
背景说明
- 需要对在线的用户进行搜索,之前是存储成 string, 每次搜索需要先全部遍历,然后加载到内存,然后进行筛选。十分消耗性能并且速度极慢。使用 redisJson + redisSearch 可以极大的优化查询性能
- 项目后期需要支持全文搜索。
-
实现思路:采用 RedisTemplate, 执行 lua 脚本。
2. 实现
2.1 配置
引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
配置 redisTermplate, 配置 lua 脚本,便于 redisTemplate 执行[^2]
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
{@Bean@SuppressWarnings(value = { "unchecked", "rawtypes" })public RedisTemplate<String, Object> redisTemplate1(RedisConnectionFactory connectionFactory){RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);RedisSearchSerializer serializer = new RedisSearchSerializer(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;}// lua 脚本配置@Beanpublic DefaultRedisScript<String> jsonSetScript(){DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();redisScript.setScriptText("return redis.call('JSON.SET', KEYS[1], '$', ARGV[1]);");redisScript.setResultType(String.class);return redisScript;}@Beanpublic DefaultRedisScript<Object> jsonGetScript(){DefaultRedisScript<Object> redisScript = new DefaultRedisScript<>();redisScript.setScriptText("return redis.call('JSON.GET', KEYS[1]);");redisScript.setResultType(Object.class);return redisScript;}@Beanpublic DefaultRedisScript<List> jsonSearchScript(){DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();redisScript.setScriptText("local offset = tonumber(ARGV[2])\n" +"local count = tonumber(ARGV[3])\n" +"return redis.call('FT.SEARCH', KEYS[1], ARGV[1], 'return', 0, 'limit', offset, count);");redisScript.setResultType(List.class);return redisScript;}
}
RedisSearchSerializer 序列化配置
import com.alibaba.fastjson2.JSON;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;import java.nio.charset.Charset;/*** Redis使用FastJson序列化** @author ruoyi*/
public class RedisSearchSerializer<T> implements RedisSerializer<T> {public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");private Class<T> clazz;public RedisSearchSerializer(Class<T> clazz) {super();this.clazz = clazz;}@Overridepublic byte[] serialize(T t) throws SerializationException {if (t == null) {return new byte[0];}if (t instanceof String) {return ((String)t).getBytes(DEFAULT_CHARSET);}return JSON.toJSONString(t).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);// 不是 json 也不是 序列化的字符串,那就只能是数字,如果不是数字直接返回if (!str.startsWith("{") && !str.startsWith("[") && !str.startsWith("\"") && !str.matches("^\\d*$")) {return clazz.cast(str);}return JSON.parseObject(str, clazz);}
}
数据实体类
@Data
public class LoginUser {private String ipaddr;private String username;public LoginUser(String ipaddr, String username) {this.ipaddr = ipaddr;this.username = username;}
}
2.2 封装 对 json的 操作
redisService
@Service
public class RedisService {@Autowiredprivate RedisScript<String> jsonSetScript;@Autowiredprivate RedisScript<Object> jsonGetScript;@Autowiredprivate RedisScript<List> jsonSearchScript;@Autowiredprivate RedisTemplate<String, Object> redisTemplate1;public LoginUser getLoginUser(String uuid) {String key = RedisKeys.LOGIN_TOKEN_KEY + uuid;JSONObject obj = (JSONObject) redisTemplate1.execute(this.jsonGetScript, Collections.singletonList(key));if (obj == null) {return null;}return obj.toJavaObject(LoginUser.class);}public void setLoginUser(String uuid, LoginUser loginUser, int expireTime, TimeUnit unit) {String key = RedisKeys.LOGIN_TOKEN_KEY + uuid;redisTemplate1.execute(this.jsonSetScript, Collections.singletonList(key), loginUser);redisCache.expire(key, expireTime, unit);}public Page<String> searchLoginUser(String query, Pageable page) {List list = redisTemplate1.execute(this.jsonSearchScript,Collections.singletonList("login_tokens_idx"),query, page.getOffset(), page.getPageSize());Long total = (Long) list.get(0);List<String> ids = new ArrayList<>();for (int i = 1; i < list.size(); i++) {ids.add(((String) list.get(i)).replaceAll(RedisKeys.LOGIN_TOKEN_KEY, ""));}return new PageImpl<>(ids, page, total);}public interface RedisKeys {String LOGIN_TOKEN_KEY = "login_tokens1:";}
}
2.3 在 redis 中创建索引
redis 创建索引[^1], 其中 ipaddr 是 IP 字段,包含 “.” 等特殊字符,所以需要将 索引中的 ipaddr 设置成 tag 类型,才能搜索到[^3]
# 连接redis, 如果使用 redisinsight 则不需要这步
redis-cli -a "password"
# 创建索引
FT.CREATE login_tokens_idx on JSON prefix 1 "login_tokens1:"SCHEMA $.ipaddr tag $.username text
3. 测试
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;import java.util.concurrent.TimeUnit;@RunWith(SpringRunner.class)
@SpringBootTest(classes = YourApplication.class)
@ActiveProfiles("prod-local")
public class RedisServiceTest {@Autowiredprivate RedisService redisService;@Testpublic void testSetAndGetLoginUser() {LoginUser user = new LoginUser("192.168.1.1", "testUser");redisService.setLoginUser("123456", user, 60, TimeUnit.SECONDS);LoginUser getUser = redisService.getLoginUser("123456");Assert.assertEquals(user.getIpaddr(), getUser.getIpaddr());Assert.assertEquals(user.getUsername(), getUser.getUsername());}@Testpublic void testDeleteLoginUser() {LoginUser user = new LoginUser("192.168.1.1", "testUser");redisService.setLoginUser("123456", user, 60, TimeUnit.SECONDS);redisService.deleteLoginUser("123456");LoginUser getUser = redisService.getLoginUser("123456");Assert.assertNull(getUser);}@Testpublic void testSearchLoginUser() {// 添加测试数据LoginUser user1 = new LoginUser("192.168.1.1", "user1");LoginUser user2 = new LoginUser("192.168.1.2", "user2");redisService.setLoginUser("123456", user1, 60, TimeUnit.SECONDS);redisService.setLoginUser("789012", user2, 60, TimeUnit.SECONDS);// 搜索测试Page<String> page = redisService.searchLoginUser("user*", PageRequest.of(0, 10));Assert.assertEquals(page.getTotalElements(), 2);Assert.assertEquals(page.getContent().size(), 2);Assert.assertTrue(page.getContent().contains("123456"));Assert.assertTrue(page.getContent().contains("789012"));}
}
相关文章:
spring boot 集成 RedisSearch 和 RedisJSON
1. 准备工作 环境说明 java 8;redis7.2.2,redis集成RedisSearch、redisJson 模块;spring boot 2.5在执行 redis 命令, 或者监控 程序执行的redis 指令时,可以采用 redisinsight查看,下载地址。 背景说明 需…...
【Kotlin精简】第8章 协程
1 简介 Kotlin 中的协程提供了一种全新处理并发的方式,您可以在 Android 平台上使用它来简化异步执行的代码。协程是从 Kotlin 1.3 版本开始引入,但这一概念在编程世界诞生的黎明之际就有了,最早使用协程的编程语言可以追溯到 1967 年的 Sim…...
【MATLAB源码-第79期】基于蚯蚓优化算法(EOA)的栅格路径规划,输出做短路径图和适应度曲线。
操作环境: MATLAB 2022a 1、算法描述 蚯蚓优化算法(Earthworm Optimisation Algorithm, EOA)是一种启发式算法,灵感来源于蚯蚓在自然界中的行为模式。蚯蚓优化算法主要模仿了蚯蚓在寻找食物和逃避天敌时的行为策略。以下是蚯蚓…...
RPC实现简单解析
RPC是什么,先摘取一段解释: RPC全称为远程过程调用(Remote Procedure Call),它是一种计算机通信协议,允许一个计算机程序调用另一个计算机上的子程序,而无需了解底层网络细节。通过RPCÿ…...
【Ubuntu】Ubuntu20.04下安装视频播放器vlc和录屏软件ssr
【Ubuntu】Ubuntu20.04下安装视频播放器vlc和录屏软件ssr 文章目录 【Ubuntu】Ubuntu20.04下安装视频播放器vlc和录屏软件ssr1. 安装视频播放器vlc2. 安装录屏软件ssr 1. 安装视频播放器vlc sudo apt-get install vlcvlc是一款比较简洁的视频播放器,如下所示 2. 安…...
WMS仓储管理系统与TMS系统整合后的优势
随着全球化的加速和供应链网络的日益复杂,仓库和运输成为企业运营中的两个关键环节。为了更高效地管理这两个环节,许多企业开始探索将WMS仓储管理系统和TMS运输管理系统整合的可能性。这种整合不仅可以提升仓库流程的可见性,还有助于改善调度…...
测试的专用
测试...
sqli-labs(Less-4) extractvalue闯关
extractvalue() - Xpath类型函数 1. 确认注入点如何闭合的方式 2. 爆出当前数据库的库名 http://127.0.0.1/sqlilabs/Less-4/?id1") and extractvalue(1,concat(~,(select database()))) --3. 爆出当前数据库的表名 http://127.0.0.1/sqlilabs/Less-4/?id1") …...
Kafka简单汇总
Kafka的结构图 多个Parttion共同组成这个topic的所有消息。每个consumer都属于一个consumer group,每条消息只能被consumer group中的一个Consumer消费, 但可以被多个consumer group消费。即组间数据是共享的,组内数据是竞争的。二、消费模型…...
任务交给谁?委派模式告诉你最佳选择!
文章目录 一、概念二、角色三、代码实现四、委派模式在源码中的体现五、委派模式的优缺点优点缺点 一、概念 委派模式(Delegate Pattern)又叫委托模式,是一种面向对象的设计模式。委派模式是一种行为模式,不属于GOF23种设计模式之中基本作用…...
【JavaEE】Servlet(创建Maven、引入依赖、创建目录、编写及打包、部署和验证、smart Tomcat)
一、什么是Servlet? Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一个 web app 1.1 Servlet能干什么? 🚕允许程序猿注册一个类, 在 Tomcat 收到某个特定的 HTTP 请求的时候, 执行这个类…...
降低城市内涝风险,万宾科技内涝积水监测仪的作用
频繁的内涝会削弱和损坏城市的关键基础设施,包括道路、桥梁和公用设施。城市内涝风险降低可以减少交通中断事件,也可以保护居民安全并降低路面维修等成本,进一步确保城市基本服务继续发挥作用。对城市可持续发展来讲有效减少内涝的风险是重要…...
水库大坝安全监测预警系统的重要作用
水库大坝建造在地质构造复杂、岩土特性不均匀的地基上,在各种荷载的作用和自然因素的影响下,其工作性态和安全状况随时都在变化。如果出现异常,又不被及时发现,其后果不堪设想。全天候实时监测,实时掌握水库水位、雨情…...
【AI视野·今日NLP 自然语言处理论文速览 第六十五期】Mon, 30 Oct 2023
AI视野今日CS.NLP 自然语言处理论文速览 Mon, 30 Oct 2023 Totally 67 papers 👉上期速览✈更多精彩请移步主页 Daily Computation and Language Papers An Approach to Automatically generating Riddles aiding Concept Attainment Authors Niharika Sri Parasa,…...
腾讯云轻量服务器购买优惠,腾讯云轻量应用服务器优惠购买方法
你是否曾经为如何选择合适的服务器而苦恼?在互联网的海洋中,如何找到一个性价比高,性能稳定,价格合理的服务器供应商,确实是一个让人头疼的问题。今天,我要向你介绍的,是腾讯云轻量应用服务器的…...
zookeeper学习记录
本文Java代码地址: https://gitee.com/blackjie_1/ljUp/tree/master/zookeeperDemo 个人博客网站:什么是快乐 基于docker 安装 拉取zookeeper 3.4.10 docker pull zookeeper:3.4.10启动服务端 docker run -d -p 2181:2181 -v /root/docker/zookeepe…...
C语言--字符串详解(多角度分析,什么是字符串?字符串如何存储?字符串如何应用?字符串常用的库函数有哪些?)
目录 一、前言 💦什么是字符串 💦字符串如何存储? 二、字符串常量和字符数组 💦字符串常量 ✨什么是字符串常量? ✨字符串常量与指针 💦字符数组 ✨字符数组的应用 💦字符串常量与字符数组的…...
【文件包含】任意文件包含的理解
谈谈任意文件包含的理解 1.漏洞描述 攻击者可以利用任意文件包含漏洞,读取文件,执行代码,对服务器造成危害。程序开发人员通常会把可重复使用函数或语句写到单个文件中,形成“封装”。在使用某个功能的时候,直接调用此…...
【ERROR】ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND No package.json
1、报错 启动项目的时候,报这个错误,是因为根目录错误,查看,根目录是否错误。...
Gitlab CI如何实现安全获取ssh-key拉取依赖项目,打包成品
文章目录 前言配置流程注册ssh-keygit runner映射文件方法1 .gitlab-ci.yml使用方法2 docker build 实现 总结 前言 之所以写这篇文章是由于存在以下场景: 当前的项目编译需要依赖别的项目协同编译,如何将别的项目也pull到该项目里,编译成品…...
Keil魔术棒设置详解:为什么你的printf在STM32上不工作?
Keil魔术棒设置详解:为什么你的printf在STM32上不工作? 调试STM32项目时,printf输出功能突然失效是许多开发者遇到的经典问题。明明代码逻辑正确,串口硬件也正常,为什么控制台就是一片寂静?这通常与Keil开…...
AI建站工具怎么选?一篇讲透选型标准与对比逻辑
面对市面上五花八门的“智能建站”、“免代码建站”宣传,很多人越看越糊涂:到底哪个才是真的适合我?是选AI自动生成的,还是拖拽式更灵活?这篇不直接给答案,而是先提供一套通用的筛选标准,再帮你…...
机械键盘连击终结者:三分钟搞定键盘“鬼键“问题
机械键盘连击终结者:三分钟搞定键盘"鬼键"问题 【免费下载链接】KeyboardChatterBlocker A handy quick tool for blocking mechanical keyboard chatter. 项目地址: https://gitcode.com/gh_mirrors/ke/KeyboardChatterBlocker 还在为机械键盘的&…...
使用 Applications Manager 实现 AWS 云监控:保障业务应用高效运行
随着企业加速将应用和服务迁移至云端,Amazon Web Services(AWS)凭借其可扩展性、可靠性及成本效益,成为主流选择。然而,云环境日益复杂,企业亟需强大的监控系统,以确保应用稳定运行。Applicatio…...
【APS合集】20余份APS生产排成系统及与其他系统集成方案合集(PPT+WORD)
本方案面向“十五五”构建以约束优化算法为核心的APS智能排程系统,通过集成ERP、MES、SCADA及WMS,实现计划-执行闭环联动。旨在解决人工排产低效、资源冲突频发等痛点,支持分钟级动态重排与交期精准承诺,显著提升设备利用率与订单…...
2025届最火的十大AI辅助论文平台横评
Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 知网AIGC检测服务是学术规范领域里较为重要的技术工具,它的核心功能是去识别学术…...
餐饮店主的AI助手:像素特工Ostrakon-VL快速上手,自动检查厨房卫生与陈列
餐饮店主的AI助手:像素特工Ostrakon-VL快速上手,自动检查厨房卫生与陈列 1. 为什么餐饮店主需要AI视觉助手 想象一下这样的场景:早上开店前,你匆匆拍下厨房的照片,上传到一个系统。几秒钟后,它告诉你&…...
从混乱到有序:ERP系统革新如何优化企业资源配置
ERP系统革新,助力企业资源配置达到最优状态在当今竞争激烈的商业环境中,企业要想脱颖而出,实现可持续发展,高效的资源配置是关键。而ERP(企业资源计划)系统的革新,正成为众多企业提升资源配置效…...
5个核心功能让你效率提升:MongoDB Compass实战指南
5个核心功能让你效率提升:MongoDB Compass实战指南 【免费下载链接】compass The GUI for MongoDB. 项目地址: https://gitcode.com/gh_mirrors/com/compass MongoDB Compass作为官方可视化管理工具,正在彻底改变开发者与MongoDB交互的方式。通过…...
Phi-3-mini-4k-instruct-gguf效果实测:单卡3090上并发3路问答的延迟与显存占用
Phi-3-mini-4k-instruct-gguf效果实测:单卡3090上并发3路问答的延迟与显存占用 1. 测试背景与模型介绍 Phi-3-mini-4k-instruct-gguf是微软Phi-3系列中的轻量级文本生成模型GGUF版本,专为问答、文本改写、摘要整理和简短创作等场景优化。作为一款开箱即…...
