SpringBoot3 + Jedis5 + Redis集群 如何通过scan方法分页获取所有keys

devtools/2025/2/9 4:37:10/

背景:

由于需要升级老项目代码,从SpringBoot1.5.x 升级到 SpringBoot3.3.x,框架中引用的Jedis自动升级到了 5.x;正好代码中有需要获取Redis集群的所有keys的需求存在;代码就不适用了,修改如下:

POM

由于默认是lettuce,需要手动添加jedis客户端

       <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><exclusions><!-- 不依赖Redis的异步客户端lettuce --><exclusion><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId></exclusion></exclusions></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency>

Jedis3.x写法

java">   public Set<String> keys(String pattern) {Set<String> set = new HashSet<>();JedisClusterConnection jedisClusterConnection = (JedisClusterConnection) redisTemplate.getConnectionFactory().getClusterConnection();//这里是获取jedispool的另外一种方式与上边的pipline可以对比下,两种方式都可以实现Map<String, JedisPool> clusterNodes = jedisClusterConnection.getNativeConnection().getClusterNodes();for (Map.Entry<String, JedisPool> entry : clusterNodes.entrySet()) {//获取单个的jedis对象Jedis jedis = entry.getValue().getResource();// 判断非从节点(因为若主从复制,从节点会跟随主节点的变化而变化),此处要使用主节点, 从主节点获取数据if (!jedis.info("replication").contains("role:slave")) {Set<String> keys = getScan(jedis, pattern);if (keys.size() > 0) {Map<Integer, Set<String>> map = new HashMap<>(8);//接下来的循环不是多余的,需要注意for (String key : keys) {// cluster模式执行多key操作的时候,这些key必须在同一个slot上,不然会报:JedisDataException:int slot = JedisClusterCRC16.getSlot(key);// 按slot将key分组,相同slot的key一起提交if (map.containsKey(slot)) {map.get(slot).add(key);} else {Set<String> set1 = new HashSet<>();set1.add(key);map.put(slot, set1);}}for (Map.Entry<Integer, Set<String>> integerListEntry : map.entrySet()) {set.addAll(integerListEntry.getValue());}}}}return set;}@SuppressWarnings({ "rawtypes", "unchecked" })public Set<String> getScan(Jedis jedis, String key) {Set<String> set = new HashSet<>();//扫描的参数对象创建与封装ScanParams params = new ScanParams();params.match(key);//扫描返回一百行,这里可以根据业务需求进行修改params.count(1000);String cursor = "0";ScanResult scanResult = jedis.scan(cursor, params);//scan.getStringCursor() 存在 且不是 0 的时候,一直移动游标获取while (null != scanResult.getCursor()) {//封装扫描的结果set.addAll(scanResult.getResult());if (!"0".equals(scanResult.getCursor())) {scanResult = jedis.scan(scanResult.getCursor(), params);} else {break;}}return set;}

Jedis5.x写法

java">    /*** 获取符合条件的key* @param pattern 表达式* @return*/public Set<String> keys(String pattern) {Set<String> set = new HashSet<>();JedisClusterConnection jedisClusterConnection = (JedisClusterConnection) redisTemplate.getConnectionFactory().getClusterConnection();//这里是获取jedispool的另外一种方式与上边的pipline可以对比下,两种方式都可以实现Map<String, ConnectionPool> clusterNodes = ((JedisCluster) jedisClusterConnection.getNativeConnection()).getClusterNodes();// 看源码获取 key就是 host:port格式Set<String> nodes = clusterNodes.keySet();for (String node : nodes) {HostAndPort nodeObj = HostAndPort.from(node);try (Jedis jedis = new Jedis(nodeObj.getHost(), nodeObj.getPort())) { // try-with-resources可以自动关闭资源String roleInfo = jedis.info("replication");if (roleInfo.contains("role:master")) {Set<String> keys = getScan(jedis, pattern);if (keys.size() > 0) {Map<Integer, Set<String>> map = new HashMap<>(8);//接下来的循环不是多余的,需要注意for (String key : keys) {// cluster模式执行多key操作的时候,这些key必须在同一个slot上,不然会报:JedisDataException:int slot = JedisClusterCRC16.getSlot(key);// 按slot将key分组,相同slot的key一起提交if (map.containsKey(slot)) {map.get(slot).add(key);} else {Set<String> set1 = new HashSet<>();set1.add(key);map.put(slot, set1);}}for (Map.Entry<Integer, Set<String>> integerListEntry : map.entrySet()) {set.addAll(integerListEntry.getValue());}}}} catch (Exception e) {logger.error("redis连接错误:", e);}}return set;}@SuppressWarnings({ "rawtypes", "unchecked" })public Set<String> getScan(Jedis jedis, String key) {Set<String> set = new HashSet<>();//扫描的参数对象创建与封装ScanParams params = new ScanParams();params.match(key);//扫描返回一百行,这里可以根据业务需求进行修改params.count(1000);String cursor = "0";ScanResult scanResult = jedis.scan(cursor, params);//scan.getStringCursor() 存在 且不是 0 的时候,一直移动游标获取while (null != scanResult.getCursor()) {//封装扫描的结果set.addAll(scanResult.getResult());if (!"0".equals(scanResult.getCursor())) {scanResult = jedis.scan(scanResult.getCursor(), params);} else {break;}}return set;}

如上几句代码修改,搞了一下午,特此记录,以备查看!

问题原因与思路

由于

java">Map<String, JedisPool> clusterNodes = jedisClusterConnection.getNativeConnection().getClusterNodes();

被修改为:

java">Map<String, ConnectionPool> clusterNodes = ((JedisCluster) jedisClusterConnection.getNativeConnection()).getClusterNodes();

而 ConnectionPool 类中包裹的 Connection类 没有JedisPool类中包裹的 Jedis类的 info()方法和 scan()方法;

所以我们可以有两个解题思路:

1.使用Connection中的sendCommand 模拟 info()方法和 scan()方法(只是猜想、未实现);

2.如上文中自己构建 Jedis对象;


http://www.ppmy.cn/devtools/157251.html

相关文章

pandas+openpyxl处理Excel

1. 读取多个 Excel 文件并合并 假设你有一个文件夹&#xff0c;里面包含多个 Excel 文件&#xff0c;你想将这些文件合并成一个 DataFrame。 import pandas as pd import os # 文件夹路径 folder_path path/to/your/excel/files # 获取文件夹中的所有 Excel 文件 excel_file…

【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-Chapter2-HTML 中的 JavaScript

二、HTML 中的 JavaScript 将 JavaScript 插入 HTML 的主要方法是使用<script>元素。 <script>元素有下列 8 个属性。 async&#xff1a;可选。表示应该立即开始下载脚本&#xff0c;但不能阻止其他页面动作&#xff0c;比如下载资源或等待其他脚本加载。只对外部…

解决错误:CondaHTTPError: HTTP 000 CONNECTION FAILED for url

解决错误&#xff1a;CondaHTTPError: HTTP 000 CONNECTION FAILED for url 查看channels:vim ~/.condarcshow_channel_urls: true channels:- http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/- http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/…

Maven 本地仓库与中央仓库

Maven 是一个强大的构建工具&#xff0c;依赖管理是其核心功能之一。在 Maven 中&#xff0c;所有的依赖包&#xff08;JAR、POM 文件等&#xff09;都存储在仓库中&#xff0c;以便项目能够快速访问和管理这些依赖。 Maven 主要使用两种类型的仓库&#xff1a; 本地仓库&…

AF3 distogram_loss函数解读

AlphaFold3 的distogram loss函数用于训练中比较预测的距离分布(由 logits 表示)与真实距离分布之间的差异。在蛋白质结构预测中,distogram 表示每对残基之间距离落在各个区间(bin)的概率分布,损失函数使用交叉熵来衡量预测分布与真实分布(通过计算残基之间的欧氏距离确…

windows部署本地deepseek

文章目录 前言一、Deepseek是什么&#xff1f;1.1 特点与优势1.2 用户反馈1.3 最新动态 二、本地部署2.1 本地部署优势2.2 硬件要求2.3 安装 Ollama2.4 下载模型2.5 安装 Chatbox&#xff08;可选&#xff09; 三 使用3.1 shell方式3.2 Chatbox 总结 前言 近年来&#xff0c;人…

【R语言】R语言安装包的相关操作

一、管理R语言安装包 1、安装R包 install.packages() 2、查看已安装的R包 installed.packages() 3、更新R包 update.packages() 4、卸载R包 remove.packages() 二、加载R语言安装包 打开R语言时&#xff0c;基础包&#xff08;base包&#xff09;会自动被加载到内存中…

玩转Docker | 使用Docker部署httpd服务

玩转Docker | 使用Docker部署httpd服务 前言一、准备工作环境确认检查操作系统准备网站目录和配置文件二、拉取httpd镜像三、运行httpd容器运行容器命令检查容器状态四、验证httpd服务浏览器访问测试错误排查五、容器管理与维护查看容器状态停止和启动容器更新网站内容和配置六…