redis中的scan命令和keys命令

keys指令和scan指令

@Test public void testScan() { Set keys = jedis.keys("*"); System.out.println(keys.toString()); int count = 3; ScanParams scanParams = new ScanParams(); ScanResult scan = jedis.scan("0", scanParams.count(count).match("*")); List 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命令 【redis中的scan命令和keys命令】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 data = https://www.it610.com/article/new HashMap(); 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 keys = redis.keys("key_*"); //将上面设值所有结果键查询出来 // 直接使用Jedis hgetall start = System.currentTimeMillis(); Map> result = new HashMap>(); 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时,需要自己保证执行命令的数据安全性。

    推荐阅读