redis String 类型的key如何批量获取?
2 个回答
慢慢理解世界,慢慢更新自己。java蚂蚁在 Redis 中,如果有一亿个 key,其中有 10 万个 key 以某个固定的已知前缀开头,以下是如何将这些 key 全部找出来的方法。 #### 数据规模和边界 - **数据规模**:一亿个 key,其中 10 万个 key 具有特定前缀。 - **边界**:必须确保查询操作不会导致服务器卡顿或内存消耗过大,适用于生产环境。 #### 解决方案 1. **使用 `keys` 指令**: - **描述**:`keys` 指令可以找出所有符合指定模式的 key 列表。 - **缺点**:`keys` 指令一次性返回所有匹配的 key,数据量过大会导致服务器卡顿,增加内存消耗和 Redis 服务器的负载,因此不适用于生产环境。 ```bash 127.0.0.1:6379> keys prefix* ``` 2. **使用 `scan` 指令**: - **描述**:`scan` 指令是基于游标的迭代器,可以无阻塞地提取指定前缀的 key 列表。 - **优点**:`scan` 每次只返回少量元素,因此适用于生产环境,不会造成服务器阻塞。 ```bash SCAN cursor [MATCH pattern] [COUNT count] ``` #### `scan` 指令详解 1. **游标机制**: - `scan` 指令基于游标的迭代器,每次调用需要传入上一次返回的游标(`cursor`)参数,延续迭代过程。 - 当 `scan` 指令的 `cursor` 参数设置为 `0` 时,表示开始新的迭代。当返回的游标值为 `0` 时,表示迭代结束。 2. **`scan` 指令的使用**: - `scan` 指令的 `MATCH` 参数可以指定要查找键的模式。 - `scan` 指令的 `COUNT` 参数是对每次返回元素数量的提示,并非严格控制。 3. **示例**: ```java 127.0.0.1:6379> scan 0 match prefix* count 10 1) "11009983" 2) 1) "prefix123" 2) "prefix456" 3) "prefix789" 127.0.0.1:6379> scan 11009983 match prefix* count 10 1) "23902345" 2) 1) "prefix234" 2) "prefix567" 3) "prefix890" 127.0.0.1:6379> scan 23902345 match prefix* count 10 1) "24080390" 2) 1) "prefix345" 2) "prefix678" 3) "prefix901" ``` #### 具体实现 以下是使用 `scan` 指令的具体实现代码示例: ```java import redis.clients.jedis.Jedis; import redis.clients.jedis.ScanParams; import redis.clients.jedis.ScanResult; import java.util.ArrayList; import java.util.List; public class RedisKeyScanner { public static List<String> scanKeys(String pattern) { Jedis jedis = new Jedis("localhost"); List<String> keys = new ArrayList<>(); String cursor = "0"; ScanParams scanParams = new ScanParams(); scanParams.match(pattern); scanParams.count(10); do { ScanResult<String> scanResult = jedis.scan(cursor, scanParams); cursor = scanResult.getCursor(); keys.addAll(scanResult.getResult()); } while (!cursor.equals("0")); return keys; } public static void main(String[] args) { List<String> keys = scanKeys("prefix*"); keys.forEach(System.out::println); } } ``` ### 总结 使用 `scan` 指令可以有效地在 Redis 中批量获取指定前缀的 key 列表。与 `keys` 指令不同,`scan` 指令适用于生产环境,因为它是增量式迭代,不会阻塞服务器或消耗过多内存。在大规模数据场景中,推荐使用 `scan` 指令来安全、有效地查找 key。编辑于 2024-04-23 14:40 •IP属地 北京举报评论收藏
纵然前路迷茫,纵然天隔一方,纵然我手中的刀刃已然破碎,也无法阻挡我寻找你的道路。小洋### 使用 RedisTemplate 批量获取 Redis 中指定前缀的 String 类型 Key 以下是使用 `RedisTemplate` 和 `SCAN` 命令来批量获取指定前缀的 Redis String 类型 Key 的具体实现。 #### 代码示例 ```java import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.Cursor; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; public class RedisKeyScanner { private RedisTemplate<String, String> redisTemplate; public RedisKeyScanner(RedisTemplate<String, String> redisTemplate) { this.redisTemplate = redisTemplate; } public List<String> scanKeys(String pattern, int perCount) { List<String> keys = new ArrayList<>(); redisTemplate.execute((RedisConnection connection) -> { try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().count(perCount).match(pattern).build())) { while (cursor.hasNext()) { String key = new String(cursor.next(), StandardCharsets.UTF_8); keys.add(key); // Do your thing with the key here if needed } } catch (IOException e) { throw new RuntimeException(e); } return null; }); return keys; } public static void main(String[] args) { // Assume RedisTemplate is configured and injected properly RedisTemplate<String, String> redisTemplate = new RedisTemplate<>(); RedisKeyScanner scanner = new RedisKeyScanner(redisTemplate); String pattern = "Team:*"; int perCount = 1000; List<String> keys = scanner.scanKeys(pattern, perCount); keys.forEach(System.out::println); } } ``` ### 解释 1. **导入相关包**: - `org.springframework.data.redis.core.RedisTemplate` - `org.springframework.data.redis.core.ScanOptions` - `org.springframework.data.redis.core.Cursor` 2. **定义扫描方法**: - `scanKeys(String pattern, int perCount)`:通过 `SCAN` 命令批量获取指定前缀的 key 列表。 - `pattern`:匹配前缀的模式,例如 `"Team:*"`。 - `perCount`:每次扫描返回的 key 的数量提示。 3. **使用 RedisTemplate 执行 SCAN 命令**: - `redisTemplate.execute((RedisConnection connection) -> { ... })`:使用 RedisTemplate 的 `execute` 方法执行 Redis 命令。 - `ScanOptions.scanOptions().count(perCount).match(pattern).build()`:构建 `ScanOptions` 对象,指定匹配模式和每次扫描返回的 key 数量提示。 4. **处理 Cursor**: - 使用 `Cursor<byte[]>` 来迭代扫描结果。 - 每次迭代时将字节数组转换为字符串,并添加到 `keys` 列表中。 - 使用 try-with-resources 语法自动关闭 `Cursor`。 5. **错误处理**: - 捕获 `IOException` 并抛出运行时异常。 ### 使用说明 - **依赖注入**:确保 `RedisTemplate` 已正确配置和注入。 - **调用示例**:在 `main` 方法中示范如何使用 `scanKeys` 方法来批量获取指定前缀的 key,并打印这些 key。 ### 总结 使用 `RedisTemplate` 和 `SCAN` 命令,可以高效地批量获取 Redis 中指定前缀的 String 类型 key。`SCAN` 命令是非阻塞的,适合在生产环境中使用,不会导致服务器卡顿或内存消耗过大。编辑于 2023-05-09 15:42 •IP属地 北京举报评论收藏
公众号进入网站体验更佳

