当前位置: 首页 > news >正文

spring boot 集成 RedisSearch 和 RedisJSON

1. 准备工作

  1. 环境说明

    • java 8;redis7.2.2,redis集成RedisSearch、redisJson 模块;spring boot 2.5
    • 在执行 redis 命令, 或者监控 程序执行的redis 指令时,可以采用 redisinsight查看,下载地址。
  2. 背景说明

    • 需要对在线的用户进行搜索,之前是存储成 string, 每次搜索需要先全部遍历,然后加载到内存,然后进行筛选。十分消耗性能并且速度极慢。使用 redisJson + redisSearch 可以极大的优化查询性能
    • 项目后期需要支持全文搜索。
  3. 实现思路:采用 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&#xff1b;redis7.2.2&#xff0c;redis集成RedisSearch、redisJson 模块&#xff1b;spring boot 2.5在执行 redis 命令&#xff0c; 或者监控 程序执行的redis 指令时&#xff0c;可以采用 redisinsight查看&#xff0c;下载地址。 背景说明 需…...

【Kotlin精简】第8章 协程

1 简介 Kotlin 中的协程提供了一种全新处理并发的方式&#xff0c;您可以在 Android 平台上使用它来简化异步执行的代码。协程是从 Kotlin 1.3 版本开始引入&#xff0c;但这一概念在编程世界诞生的黎明之际就有了&#xff0c;最早使用协程的编程语言可以追溯到 1967 年的 Sim…...

【MATLAB源码-第79期】基于蚯蚓优化算法(EOA)的栅格路径规划,输出做短路径图和适应度曲线。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 蚯蚓优化算法&#xff08;Earthworm Optimisation Algorithm, EOA&#xff09;是一种启发式算法&#xff0c;灵感来源于蚯蚓在自然界中的行为模式。蚯蚓优化算法主要模仿了蚯蚓在寻找食物和逃避天敌时的行为策略。以下是蚯蚓…...

RPC实现简单解析

RPC是什么&#xff0c;先摘取一段解释&#xff1a; RPC全称为远程过程调用&#xff08;Remote Procedure Call&#xff09;&#xff0c;它是一种计算机通信协议&#xff0c;允许一个计算机程序调用另一个计算机上的子程序&#xff0c;而无需了解底层网络细节。通过RPC&#xff…...

【Ubuntu】Ubuntu20.04下安装视频播放器vlc和录屏软件ssr

【Ubuntu】Ubuntu20.04下安装视频播放器vlc和录屏软件ssr 文章目录 【Ubuntu】Ubuntu20.04下安装视频播放器vlc和录屏软件ssr1. 安装视频播放器vlc2. 安装录屏软件ssr 1. 安装视频播放器vlc sudo apt-get install vlcvlc是一款比较简洁的视频播放器&#xff0c;如下所示 2. 安…...

WMS仓储管理系统与TMS系统整合后的优势

随着全球化的加速和供应链网络的日益复杂&#xff0c;仓库和运输成为企业运营中的两个关键环节。为了更高效地管理这两个环节&#xff0c;许多企业开始探索将WMS仓储管理系统和TMS运输管理系统整合的可能性。这种整合不仅可以提升仓库流程的可见性&#xff0c;还有助于改善调度…...

测试的专用

测试...

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&#xff0c;每条消息只能被consumer group中的一个Consumer消费&#xff0c; 但可以被多个consumer group消费。即组间数据是共享的&#xff0c;组内数据是竞争的。二、消费模型…...

任务交给谁?委派模式告诉你最佳选择!

文章目录 一、概念二、角色三、代码实现四、委派模式在源码中的体现五、委派模式的优缺点优点缺点 一、概念 委派模式&#xff08;Delegate Pattern)又叫委托模式&#xff0c;是一种面向对象的设计模式。委派模式是一种行为模式&#xff0c;不属于GOF23种设计模式之中基本作用…...

【JavaEE】Servlet(创建Maven、引入依赖、创建目录、编写及打包、部署和验证、smart Tomcat)

一、什么是Servlet&#xff1f; Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一个 web app 1.1 Servlet能干什么&#xff1f; &#x1f695;允许程序猿注册一个类, 在 Tomcat 收到某个特定的 HTTP 请求的时候, 执行这个类…...

降低城市内涝风险,万宾科技内涝积水监测仪的作用

频繁的内涝会削弱和损坏城市的关键基础设施&#xff0c;包括道路、桥梁和公用设施。城市内涝风险降低可以减少交通中断事件&#xff0c;也可以保护居民安全并降低路面维修等成本&#xff0c;进一步确保城市基本服务继续发挥作用。对城市可持续发展来讲有效减少内涝的风险是重要…...

水库大坝安全监测预警系统的重要作用

水库大坝建造在地质构造复杂、岩土特性不均匀的地基上&#xff0c;在各种荷载的作用和自然因素的影响下&#xff0c;其工作性态和安全状况随时都在变化。如果出现异常&#xff0c;又不被及时发现&#xff0c;其后果不堪设想。全天候实时监测&#xff0c;实时掌握水库水位、雨情…...

【AI视野·今日NLP 自然语言处理论文速览 第六十五期】Mon, 30 Oct 2023

AI视野今日CS.NLP 自然语言处理论文速览 Mon, 30 Oct 2023 Totally 67 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers An Approach to Automatically generating Riddles aiding Concept Attainment Authors Niharika Sri Parasa,…...

腾讯云轻量服务器购买优惠,腾讯云轻量应用服务器优惠购买方法

你是否曾经为如何选择合适的服务器而苦恼&#xff1f;在互联网的海洋中&#xff0c;如何找到一个性价比高&#xff0c;性能稳定&#xff0c;价格合理的服务器供应商&#xff0c;确实是一个让人头疼的问题。今天&#xff0c;我要向你介绍的&#xff0c;是腾讯云轻量应用服务器的…...

zookeeper学习记录

本文Java代码地址&#xff1a; https://gitee.com/blackjie_1/ljUp/tree/master/zookeeperDemo 个人博客网站&#xff1a;什么是快乐 基于docker 安装 拉取zookeeper 3.4.10 docker pull zookeeper:3.4.10启动服务端 docker run -d -p 2181:2181 -v /root/docker/zookeepe…...

C语言--字符串详解(多角度分析,什么是字符串?字符串如何存储?字符串如何应用?字符串常用的库函数有哪些?)

目录 一、前言 &#x1f4a6;什么是字符串 &#x1f4a6;字符串如何存储&#xff1f; 二、字符串常量和字符数组 &#x1f4a6;字符串常量 ✨什么是字符串常量&#xff1f; ✨字符串常量与指针 &#x1f4a6;字符数组 ✨字符数组的应用 &#x1f4a6;字符串常量与字符数组的…...

【文件包含】任意文件包含的理解

谈谈任意文件包含的理解 1.漏洞描述 攻击者可以利用任意文件包含漏洞&#xff0c;读取文件&#xff0c;执行代码&#xff0c;对服务器造成危害。程序开发人员通常会把可重复使用函数或语句写到单个文件中&#xff0c;形成“封装”。在使用某个功能的时候&#xff0c;直接调用此…...

【ERROR】ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND No package.json

1、报错 启动项目的时候&#xff0c;报这个错误&#xff0c;是因为根目录错误&#xff0c;查看&#xff0c;根目录是否错误。...

Gitlab CI如何实现安全获取ssh-key拉取依赖项目,打包成品

文章目录 前言配置流程注册ssh-keygit runner映射文件方法1 .gitlab-ci.yml使用方法2 docker build 实现 总结 前言 之所以写这篇文章是由于存在以下场景&#xff1a; 当前的项目编译需要依赖别的项目协同编译&#xff0c;如何将别的项目也pull到该项目里&#xff0c;编译成品…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

在 Spring Boot 项目里,MYSQL中json类型字段使用

前言&#xff1a; 因为程序特殊需求导致&#xff0c;需要mysql数据库存储json类型数据&#xff0c;因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...

GraphQL 实战篇:Apollo Client 配置与缓存

GraphQL 实战篇&#xff1a;Apollo Client 配置与缓存 上一篇&#xff1a;GraphQL 入门篇&#xff1a;基础查询语法 依旧和上一篇的笔记一样&#xff0c;主实操&#xff0c;没啥过多的细节讲解&#xff0c;代码具体在&#xff1a; https://github.com/GoldenaArcher/graphql…...

图解JavaScript原型:原型链及其分析 | JavaScript图解

​​ 忽略该图的细节&#xff08;如内存地址值没有用二进制&#xff09; 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么&#xff1a;保存在堆中一块区域&#xff0c;同时在栈中有一块区域保存其在堆中的地址&#xff08;也就是我们通常说的该变量指向谁&…...