redis集群模式遍历寻找符合规则的key

713 阅读2分钟

项目中使用了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的数据。