Redis中有一个经典的问题,在巨大的数据量的情况下,做类似于查找符合某种规则的Key的信息,这里就有两种方式,
一是keys命令,简单粗暴,由于Redis单线程这一特性,keys命令是以阻塞的方式执行的,keys是以遍历的方式实现的复杂度是 O(n),Redis库中的key越多,查找实现代价越大,产生的阻塞时间越长。
二是scan命令,以非阻塞的方式实现key值的查找,绝大多数情况下是可以替代keys命令的,可选性更强
- 127.0.0.1:6379> keys s*
- 1) "s1"
- 2) "sddddf"
- 3) "sss"
- 4) "s358"
- 127.0.0.1:6379>
- SCAN cursor [MATCH pattern] [COUNT count]
- cursor - 游标。
- pattern - 匹配的模式。
- count - 指定从数据集里返回多少元素,默认值为 10 。
- 可用版本
- >= 2.8.0
-
-
示例:
- 127.0.0.1:6379> keys s*
- 1) "s1"
- 2) "sddddf"
- 3) "sss"
- 4) "s358"
- 127.0.0.1:6379> scan 0 match s* count 20
- 1) "0"
- 2) 1) "s1"
- 2) "sss"
- 3) "s358"
- 4) "sddddf"
- 127.0.0.1:6379> scan 0 match s* count 2
- 1) "4"
- 2) 1) "s1"
- 127.0.0.1:6379> scan 4 match s* count 2
- 1) "1"
- 2) 1) "sss"
- 2) "s358"
- 127.0.0.1:6379> scan 1 match s* count 2
- 1) "3"
- 2) 1) "sddddf"
SCAN 命令是一个基于游标的迭代器,每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。
- public Set<String> scan(String matchKey) {
- Set<String> keys = redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
- Set<String> keysTmp = new HashSet<>();
- Cursor<byte[]> cursor = connection.scan(
- new ScanOptions.ScanOptionsBuilder().match("*" + matchKey + "*").count(1000).build()
- );
- while (cursor.hasNext()) {
- keysTmp.add(new String(cursor.next()));
- }
- return keysTmp;
- });
-
- return keys;
- }