redis中的scan命令和keys命令

keys指令和scan指令

    @Test
    public void testScan() {
        Set<String> keys = jedis.keys("*");
        System.out.println(keys.toString());
        int count = 3;
        ScanParams scanParams = new ScanParams();
        ScanResult<String> scan = jedis.scan("0", scanParams.count(count).match("*"));
        List<String> list = new ArrayList<>(scan.getResult());
        for (; ; ) {
            String stringCursor = scan.getStringCursor();
            //当游标为0时说明遍历完成,退出循环
            if (stringCursor.equals("0")) {

                break;
            } else {
                scan = jedis.scan(stringCursor, scanParams.count(count).match("*"));
                System.out.println("---"+scan.getResult().toString());
                list.addAll(scan.getResult());
            }
        }
        System.out.println(list.toString());
//        jedis.scan("0");
//        jedis.hscan("myHash", "0");
    }
1 keys命令

可以使用正则查找匹配的结果。
该命令有致命的缺点

  1. 没有limit,只能一次性获取所有符合条件的key。如果数据量很大的话,就会产生无穷无尽的输出。
  2. keys命令是遍历算法,遍历全部的key,时间复杂度是O(N)。redis是单线程的,如果keys查询的时间过长,redis的其它操作会被阻塞较长时间,造成redis较长时间的卡顿。要避免在生产环境使用该命令。
2 scan命令

redis2.8版本之后,可以使用scan命令进行正则匹配查找。与keys不同的是,scan命令不是一次性查找出所有满足条件的key。而是根据游标和遍历的字典槽数,使得每次查询都限制在一定范围内。
cursor = 0时表示开始查询,第一次查询。每次查询结果中都会返回一个cursor作为下次查询的开始游标。当某次查询返回的cursor=0时,表示已全部查询完毕。
相比于keys命令,scan命令有两个比较明显的优势

  1. scan命令的时间复杂度虽然也是O(N),但它是分次进行的,不会阻塞线程。
  2. scan命令提供了limit参数,可以控制每次返回结果的最大条数。

scan的缺点:
返回的数据有可能重复,需要我们在业务层按需要去重

Redis中的Pipeline

Redis的管道可以在大量数据需要一次性操作完成的时候,使用Pipeline进行批处理,将一大队的操作合并成一次操作,可以减少链路层的时间消耗,毕竟频繁操作是不好的嘛.


    @Test
    public void pipeCompare() {
        Jedis redis = jedis;
        //redis.auth("12345678");//授权密码 对应redis.conf的requirepass密码
        Map<String, String> data = new HashMap<String, String>();
        redis.select(8);//使用第8个库
        redis.flushDB();//清空第8个库所有数据
        // hmset
        long start = System.currentTimeMillis();
        // 直接hmset
        for (int i = 0; i < 10000; i++) {
            data.clear();  //清空map
            data.put("k_" + i, "v_" + i);
            redis.hmset("key_" + i, data); //循环执行10000条数据插入redis
        }
        long end = System.currentTimeMillis();
        System.out.println("    共插入:[" + redis.dbSize() + "]条 .. ");
        System.out.println("1,未使用PIPE批量设值耗时" + (end - start)  + "毫秒..");
        redis.select(8);
        redis.flushDB();
        // 使用pipeline hmset
        Pipeline pipe = redis.pipelined();
        start = System.currentTimeMillis();
        //
        for (int i = 0; i < 10000; i++) {
            data.clear();
            data.put("k_" + i, "v_" + i);
            pipe.hmset("key_" + i, data); //将值封装到PIPE对象,此时并未执行,还停留在客户端
        }
        pipe.sync(); //将封装后的PIPE一次性发给redis
        end = System.currentTimeMillis();
        System.out.println("    PIPE共插入:[" + redis.dbSize() + "]条 .. ");
        System.out.println("2,使用PIPE批量设值耗时" + (end - start)+ "毫秒 ..");
//--------------------------------------------------------------------------------------------------
        // hmget
        Set<String> keys = redis.keys("key_*"); //将上面设值所有结果键查询出来
        // 直接使用Jedis hgetall
        start = System.currentTimeMillis();
        Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>();
        for (String key : keys) {
            //此处keys根据以上的设值结果,共有10000个,循环10000次
            result.put(key, redis.hgetAll(key)); //使用redis对象根据键值去取值,将结果放入result对象
        }
        end = System.currentTimeMillis();
        System.out.println("    共取值:[" + redis.dbSize() + "]条 .. ");
        System.out.println("3,未使用PIPE批量取值耗时 " + (end - start)+ "毫秒..");

        // 使用pipeline hgetall
        result.clear();
        start = System.currentTimeMillis();
        for (String key : keys) {
            pipe.hgetAll(key); //使用PIPE封装需要取值的key,此时还停留在客户端,并未真正执行查询请求
        }
        pipe.sync();  //提交到redis进行查询

        end = System.currentTimeMillis();
        System.out.println("    PIPE共取值:[" + redis.dbSize() + "]条 .. ");
        System.out.println("4,使用PIPE批量取值耗时" + (end - start)  + "毫秒 ..");

        redis.disconnect();
    }

pipeline注意点
  1. pipeline只适用于那些不需要获取同步结果的场景,比如hincr,hset等更新操作。而对于读取hget操作则不能适用。
  2. pipeline组装命令也不能是没有节制的,如果pipeline组装命令数据过多,则会导致一次pipeline同步等待时间过长,影响客户端体验甚至导致网络阻塞。
  3. pipeline不能保证命令执行的原子性。如多个命令在执行的中间发生了异常,那么将会丢失未执行的命令。所以我们一般使用pipeline时,需要自己保证执行命令的数据安全性。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值