项目中使用了redis集群模式,key分布在不同的节点上,想要寻找符合某个pattern的所有的key变得比较麻烦。
在redis集群模式下,数据分布在不同的槽中,不同的节点负责不同的槽范围,查询某个键时会根据键的哈希值计算出槽,并将请求路由到负责该槽的节点上。
集群模式中查找确定的某个key值没什么需要特别处理的,但是在集群模式中要查找符合某个规则的所有key比较麻烦。在 Redis 集群中,由于数据被分片存储在不同的槽(slot)上,所以在单个节点上执行SCAN命令并不能实现全局的键的扫描,需要遍历每个节点,在每个节点上执行SCAN命令来查找匹配的键。
在每个节点的控制台中遍历寻找key比较麻烦,可以通过代码来实现。
Jedis实现代码如下:
package com.aaron.utils;
import redis.clients.jedis.*;
import java.util.HashSet;
import java.util.Set;
/**
* @author liurong
* @version 1.0
* @date 2023/7/12 14:15
*/public class RedisUtils {
public static void scanClusterRedisNodes(Set<HostAndPort> jedisClusterNodes, String password, String matchStr) {
// 创建 JedisCluster 对象
JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes, 15000, 15000, 5, password, new JedisPoolConfig());
// 执行 SCAN 命令遍历键空间
ScanParams scanParams = new ScanParams().match(matchStr).count(100); // 匹配以 EQUIPMENT_INFO 开头的键
Set<String> equipmentInfoKeys = new HashSet<>();
for (JedisPool jedisPool : jedisCluster.getClusterNodes().values()) {
try (Jedis jedis = jedisPool.getResource()) {
String cursor = ScanParams.SCAN_POINTER_START;
do {
// 执行 SCAN 命令
ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
cursor = scanResult.getCursor();
equipmentInfoKeys.addAll(scanResult.getResult());
} while (!ScanParams.SCAN_POINTER_START.equals(cursor)); // 当游标为初始值时,表示遍历结束
}
}
// 输出满足条件的键
for (String key : equipmentInfoKeys) {
System.out.println(key);
}
// 关闭 JedisCluster 连接
jedisCluster.close();
}
public static void main(String[] args) {
// 创建 Redis 集群节点的 Set
Set<HostAndPort> jedisClusterNodes = new HashSet<>();
jedisClusterNodes.add(new HostAndPort("172.32.0.92", 7001));
jedisClusterNodes.add(new HostAndPort("172.32.0.92", 7003));
jedisClusterNodes.add(new HostAndPort("172.32.0.92", 7005));
scanClusterRedisNodes(jedisClusterNodes, "password", "EQUIPMENT_INFO*");
}
}
在redis控制台中查找匹配的键值,使用scan命令:
SCAN cursor [MATCH pattern] [COUNT count]
- cursor - 游标。
- pattern - 匹配的模式。
- count - 可选,用于指定每次迭代返回的 key 的数量,默认值为 10 。
示例:
~~~ sh
>scan 0
3584
xxx1
xxx2
>scan 0 count 100
2656
xxx3
xxx4
>scan 0 match EQUIPMENT_INFO* count 100
2656
xxx
第一个3584标识游标值 游标值与数据条数之间没有直接关系。
在 Redis 的 SCAN 命令中,游标值用于标识扫描的位置和状态,以支持逐步迭代地遍历键空间。每次执行 SCAN 命令,都会返回一个新的游标值作为下一次扫描的起始位置。
游标值不表示数据的总数或当前扫描的位置。它只是一个用于迭代扫描的标识符。通过连续执行 SCAN 命令,并使用上一次命令返回的游标值作为下一次命令的参数,可以逐步遍历整个键空间。
scan 0 ,0是默认初始游标。
指定的count数量不一样,返回的迭代游标值也不一样。比如上面示例中每次scan 0得到的下次游标值是一样的,scan 0 count 100后游标值变化。在scan时指定match pattern并不影响游标值,加了pattern只是会过滤只返回符合pattern的数据。