zoukankan      html  css  js  c++  java
  • 面试突击 | Redis 如何从海量数据中查询出某一个 Key?附视频

    1 考察知识点

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

    1. Keys 和 Scan 的区别
    2. Keys 查询的缺点
    3. Scan 如何使用?
    4. Scan 查询的特点

    2 解答思路

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

    3 Keys 使用相关

    1)Keys 用法如下

    2)Keys 存在的问题

    1. 此命令没有分页功能,我们只能一次性查询出所有符合条件的 key 值,如果查询结果非常巨大,那么得到的输出信息也会非常多;
    2. 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)
    

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

    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 的查询功能,代码如下:

    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 具有以下特征:

    1. Scan 可以实现 keys 的匹配功能;
    2. Scan 是通过游标进行查询的不会导致 Redis 假死;
    3. Scan 提供了 count 参数,可以规定遍历的数量;
    4. Scan 会把游标返回给客户端,用户客户端继续遍历查询;
    5. Scan 返回的结果可能会有重复数据,需要客户端去重;
    6. 单次返回空值且游标不为 0,说明遍历还没结束;
    7. Scan 可以保证在开始检索之前,被删除的元素一定不会被查询出来;
    8. 在迭代过程中如果有元素被修改, Scan 不保证能查询出相关的元素。

    7 视频版

    视频版:https://www.bilibili.com/video/av88076985/

  • 相关阅读:
    【SCOI 2011】 糖果
    【POJ 3159】 Candies
    【POJ 1716】 Integer Intervals
    【POJ 2983】 Is the information reliable?
    【POJ 1364】 King
    【POJ 1201】 Intervals
    【POJ 1804】 Brainman
    6月10日省中提高组题解
    【POJ 3352】 Road Construction
    【POJ 1144】 Network
  • 原文地址:https://www.cnblogs.com/vipstone/p/12373734.html
Copyright © 2011-2022 走看看