面试突击 | Redis 如何从海量数据中查询出某一个 Key?附视频

1 考察知识点
本题考察的知识点有以下几个:
- Keys 和 Scan 的区别
- Keys 查询的缺点
- Scan 如何使用?
- Scan 查询的特点
2 解答思路
- Keys 查询存在的问题
- Scan 的使用
- Scan 的特点
3 Keys 使用相关
1)Keys 用法如下

2)Keys 存在的问题
- 此命令没有分页功能,我们只能一次性查询出所有符合条件的 key 值,如果查询结果非常巨大,那么得到的输出信息也会非常多;
- keys 命令是遍历查询,因此它的查询时间复杂度是 o(n),所以数据量越大查询时间就越长。
4 Scan 使用相关
我们先来模拟海量数据,使用 Pipeline 添加 10w 条数据,Java 代码实现如下:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import utils.JedisUtils;
public class ScanExample {
public static void main(String[] args) {
// 添加 10w 条数据
initData();
}
public static void initData(){
Jedis jedis = JedisUtils.getJedis();
Pipeline pipe = jedis.pipelined();
for (int i = 1; i < 100001; i++) {
pipe.set("user_token_" + i, "id" + i);
}
// 执行命令
pipe.sync();
System.out.println("数据插入完成");
}
}
我们来查询用户 id 为 9999* 的数据,Scan 命令使用如下:
127.0.0.1:6379> scan 0 match user_token_9999* count 10000
1) "127064"
2) 1) "user_token_99997"
127.0.0.1:6379> scan 127064 match user_token_9999* count 10000
1) "1740"
2) 1) "user_token_9999"
127.0.0.1:6379> scan 1740 match user_token_9999* count 10000
1) "21298"
2) 1) "user_token_99996"
127.0.0.1:6379> scan 21298 match user_token_9999* count 10000
1) "65382"
2) (empty list or set)
127.0.0.1:6379> scan 65382 match user_token_9999* count 10000
1) "78081"
2) 1) "user_token_99998"
2) "user_token_99992"
127.0.0.1:6379> scan 78081 match user_token_9999* count 10000
1) "3993"
2) 1) "user_token_99994"
2) "user_token_99993"
127.0.0.1:6379> scan 3993 match user_token_9999* count 10000
1) "13773"
2) 1) "user_token_99995"
127.0.0.1:6379> scan 13773 match user_token_9999* count 10000
1) "47923"
2) (empty list or set)
127.0.0.1:6379> scan 47923 match user_token_9999* count 10000
1) "59751"
2) 1) "user_token_99990"
2) "user_token_99991"
3) "user_token_99999"
127.0.0.1:6379> scan 59751 match user_token_9999* count 10000
1) "0"
2) (empty list or set)
从以上的执行结果,我们看出两个问题:
- 查询的结果为空,但游标值不为 0,表示遍历还没结束;
- 设置的是 count 10000,但每次返回的数量都不是 10000,且不固定,这是因为 count 只是限定服务器单次遍历的字典槽位数量 (约等于),而不是规定返回结果的 count 值。
相关语法:scan cursor [MATCH pattern] [COUNT count]
其中:
- cursor:光标位置,整数值,从 0 开始,到 0 结束,查询结果是空,但游标值不为 0,表示遍历还没结束;
- match pattern:正则匹配字段;
- count:限定服务器单次遍历的字典槽位数量 (约等于),只是对增量式迭代命令的一种提示 (hint),并不是查询结果返回的最大数量,它的默认值是 10。
5 Scan 代码实战
本文我们使用 Java 代码来实现 Scan 的查询功能,代码如下:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;
import utils.JedisUtils;
public class ScanExample {
public static void main(String[] args) {
Jedis jedis = JedisUtils.getJedis();
// 定义 match 和 count 参数
ScanParams params = new ScanParams();
params.count(10000);
params.match("user_token_9999*");
// 游标
String cursor = "0";
while (true) {
ScanResult<String> res = jedis.scan(cursor, params);
if (res.getCursor().equals("0")) {
// 表示最后一条
break;
}
cursor = res.getCursor(); // 设置游标
for (String item : 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 具有以下特征:
- Scan 可以实现 keys 的匹配功能;
- Scan 是通过游标进行查询的不会导致 Redis 假死;
- Scan 提供了 count 参数,可以规定遍历的数量;
- Scan 会把游标返回给客户端,用户客户端继续遍历查询;
- Scan 返回的结果可能会有重复数据,需要客户端去重;
- 单次返回空值且游标不为 0,说明遍历还没结束;
- Scan 可以保证在开始检索之前,被删除的元素一定不会被查询出来;
- 在迭代过程中如果有元素被修改, Scan 不保证能查询出相关的元素。
7 视频版
视频版:https://www.bilibili.com/video/av88076985/
面试突击 | Redis 如何从海量数据中查询出某一个 Key?附视频的更多相关文章
- Redis实战(20)Redis 如何从海量数据中查询出某一个 Key?
序言 资料 https://www.cnblogs.com/vipstone/p/12373734.html
- [翻译] C# 8.0 新特性 Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南) 【由浅至深】redis 实现发布订阅的几种方式 .NET Core开发者的福音之玩转Redis的又一傻瓜式神器推荐
[翻译] C# 8.0 新特性 2018-11-13 17:04 by Rwing, 1179 阅读, 24 评论, 收藏, 编辑 原文: Building C# 8.0[译注:原文主标题如此,但内容 ...
- 海量数据中找出前k大数(topk问题)
海量数据中找出前k大数(topk问题) 前两天面试3面学长问我的这个问题(想说TEG的3个面试学长都是好和蔼,希望能完成最后一面,各方面原因造成我无比想去鹅场的心已经按捺不住了),这个问题还是建立最小 ...
- 从海量数据中寻找出topK的最优算法代码
package findMinNumIncludedTopN;/** * 小顶堆 * @author TongXueQiang * @date 2016/03/09 * @since JDK 1.8 ...
- 原创:从海量数据中查找出前k个最小或最大值的算法(java)
现在有这么一道题目:要求从多个的数据中查找出前K个最小或最大值 分析:有多种方案可以实现.一.最容易想到的是先对数据快速排序,然后输出前k个数字. 二.先定义容量为k的数组,从源数据中取出前k个填 ...
- Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南)
作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9941208.html 主讲人:大石头 时间:2018-11-10 晚上20:00 地点:钉钉群(组织代码 ...
- sql语句中查询出的数据添加一列,并且添加默认值
查询出数据,并且要添加一列表中都不存在的数据,且这一列的值都是相等的 select app_id,app_secret from wx_ticket group by app_id; 查询出的数据是 ...
- ybatis中查询出多个以key,value的属性记录,封装成一个map返回的方法
可以采用值做映射,也可以不采用映射方式 <resultMap id="configMap" type="java.util.Map" > <r ...
- Redis使用场景一,查询出的数据保存到Redis中,下次查询的时候直接从Redis中拿到数据。不用和数据库进行交互。
maven使用: <!--redis jar包--> <dependency> <groupId>redis.clients</groupId> < ...
随机推荐
- cors中间件
class MiddlewareMixin(object): def __init__(self, get_response=None): self.get_response = get_respon ...
- Go的http包中默认路由匹配规则
# 一.执行流程 首先我们构建一个简单http server: ```go package main import ( "log" "net/http" ) f ...
- C语言进阶——全局变量
全局变量 ·定义在函数外面的变量是全局变量 ·全局变量具有全局的生存期和作用域 ·它们与任何函数都无关 ·在任何函数内部都可以使用它们 全局变量初始化 ·没有做初始化的全局变量会得到0值 ·指针会得到 ...
- Dynamics 365 CRM Connected Field Service 不能接收IoT Alert
今天浪费了2,3个小时再connected field service(CFS)上面. 状况如下 1. 在CFS中添加了新的customer assets,并且点击了注册按钮. 2. 注册步骤一直在i ...
- .NET Core微服务二:Ocelot API网关
.NET Core微服务一:Consul服务中心 .NET Core微服务二:Ocelot API网关 .NET Core微服务三:polly熔断与降级 本文的项目代码,在文章结尾处可以下载. 本文使 ...
- hash算法与拉链法解决冲突
<?php class HashNode { public $key; public $value; public $nextNode; public function __construct( ...
- 云原生 - Istio可观察性之分布式跟踪(三)
作者:justmine 头条号:大数据与云原生 微信公众号:大数据与云原生 创作不易,在满足创作共用版权协议的基础上可以转载,但请以超链接形式注明出处. 为了方便阅读,微信公众号已按分类排版,后续的文 ...
- demon病毒样本分析
1. 简介 该样本是前几周爆发的THINKPHP漏洞中,被批量上传的一个病毒样本.如图所示. 2. 分析 该样本未经混淆,加壳,所以直接拖到IDA中即可分析. 首先从main函数开始.做一些初始化的函 ...
- ORALCE逻辑存储结构
ORACLE逻辑存储结构块: 数据块 他是最基础的逻辑存储单元,数据以行的形式存储到我么的数据块中 区 :多个块的集合 并且区组成了物理的数据文件 段 :(表 索引 物化视图 物化视图日志 大对象 大 ...
- 10、VLAN
VLAN综述:1.分段2.灵活性3.安全性 一个VLAN=一个广播域=逻辑网段(子网) VLAN运作:1.每个逻辑的VLAN就象一个独立的物理桥2.交换机上的每一个端口都可以分配给不同的VLAN3.默 ...