redis百万数据查询效率 「redis模糊查询key的数量」

  • redis百万数据查询效率 「redis模糊查询key的数量」已关闭评论
  • 157 次浏览
  • A+
所属分类:随笔创作

1 考察知识点

本题考察的知识点有以下几个:

Keys 和 Scan 的区别Keys 查询的缺点Scan 如何使用?Scan 查询的特点

2 解答思路

1. Keys 查询存在的问题2. Scan 的使用3. Scan 的特点

3 Keys 使用相关

1)Keys 用法如下

2)Keys 存在的问题

此命令没有分页功能,我们只能一次性查询出所有符合条件的 key 值,如果查询结果非常巨大,那么得到的输出信息也会非常多;keys 命令是遍历查询,因此它的查询时间复杂度是 o(n),所以数据量越大查询时间就越长。

4 Scan 使用相关

我们先来模拟海量数据,使用 Pipeline 添加 10w 条数据,Java 代码实现如下:

importredis.clients.jedis.Jedis;importredis.clients.jedis.Pipeline;importutils.JedisUtils;publicclassScanExample{publicstaticvoidmain(String[] args){// 添加 10w 条数据initData();
}publicstaticvoidinitData(){
Jedis jedis = JedisUtils.getJedis();
Pipeline pipe = jedis.pipelined();for(inti =1; i <100001; i++) {
pipe.set("user_token_"+ i,"id"+ i);
}// 执行命令pipe.sync();
System.out.println("数据插入完成");
}
}

我们来查询用户 id 为 9999* 的数据,Scan 命令使用如下:

127.0.0.1:6379>scan0matchuser_token_9999*count100001)"127064"2)1)"user_token_99997"127.0.0.1:6379>scan127064matchuser_token_9999*count100001)"1740"2)1)"user_token_9999"127.0.0.1:6379>scan1740matchuser_token_9999*count100001)"21298"2)1)"user_token_99996"127.0.0.1:6379>scan21298matchuser_token_9999*count100001)"65382"2)(emptylistorset)127.0.0.1:6379>scan65382matchuser_token_9999*count100001)"78081"2)1)"user_token_99998"2)"user_token_99992"127.0.0.1:6379>scan78081matchuser_token_9999*count100001)"3993"2)1)"user_token_99994"2)"user_token_99993"127.0.0.1:6379>scan3993matchuser_token_9999*count100001)"13773"2)1)"user_token_99995"127.0.0.1:6379>scan13773matchuser_token_9999*count100001)"47923"2)(emptylistorset)127.0.0.1:6379>scan47923matchuser_token_9999*count100001)"59751"2)1)"user_token_99990"2)"user_token_99991"3)"user_token_99999"127.0.0.1:6379>scan59751matchuser_token_9999*count100001)"0"2)(emptylistorset)

从以上的执行结果,我们看出两个问题:

1. 查询的结果为空,但游标值不为 0,表示遍历还没结束;

2. 设置的是 count 10000,但每次返回的数量都不是 10000,且不固定,这是因为 count 只是限定

服务器单次遍历的字典槽位数量 (约等于),而不是规定返回结果的 count 值。

相关语法:scan cursor [MATCH pattern] [COUNT count]

其中:

cursor:光标位置,整数值,从 0 开始,到 0 结束,查询结果是空,但游标值不为 0,表示遍历还没结束;match pattern:正则匹配字段;count:限定服务器单次遍历的字典槽位数量 (约等于),只是对增量式迭代命令的一种提示(hint),并不是查询结果返回的最大数量,它的默认值是 10。

5 Scan 代码实战

本文我们使用 Java 代码来实现 Scan 的查询功能,代码如下:

importredis.clients.jedis.Jedis;importredis.clients.jedis.Pipeline;importredis.clients.jedis.ScanParams;importredis.clients.jedis.ScanResult;importutils.JedisUtils;publicclassScanExample{publicstaticvoid main(String[] args) {Jedisjedis =JedisUtils.getJedis();// 定义 match 和 count 参数ScanParamsparams = newScanParams();
params.count(10000);
params.match("user_token_9999*");// 游标Stringcursor ="0";while(true) {ScanResult<String> res = jedis.scan(cursor, params);if(res.getCursor().equals("0")) {// 表示最后一条break;
}
cursor = res.getCursor();// 设置游标for(Stringitem : res.getResult()) {// 打印查询结果System.out.println("查询结果:"+ item);
}
}
}
}

以上程序执行结果如下:

查询结果:user_token_99997

查询结果:user_token_9999

查询结果:user_token_99996

查询结果:user_token_99998

查询结果:user_token_99992

查询结果:user_token_99994

查询结果:user_token_99993

查询结果:user_token_99995

查询结果:user_token_99990

查询结果:user_token_99991

查询结果:user_token_99999

6 总结

通过本文我们了解到,Redis 中如果要在海量的数据数据中,查询某个数据应该使用 Scan,Scan 具有以下特征:

1. Scan 可以实现 keys 的匹配功能;

2. Scan 是通过游标进行查询的不会导致 Redis 假死;

3. Scan 提供了 count 参数,可以规定遍历的数量;

4. Scan 会把游标返回给客户端,用户客户端继续遍历查询;

5. Scan 返回的结果可能会有重复数据,需要客户端去重;

6. 单次返回空值且游标不为 0,说明遍历还没结束;

7. Scan 可以保证在开始检索之前,被删除的元素一定不会被查询出来;

8. 在迭代过程中如果有元素被修改, Scan 不保证能查询出相关的元素。

7 视频版

视频内容如下:

重播播放00:00/00:00正在直播00:00进入全屏50点击按住可拖动视频

夜行书生