hello,大家好,我是小慕,今天我们一起继续学习Redis的知识吧!面试官:“Redis的keys和scan命令有什么区别?”_主线程面试官:“Redis的keys和scan命令有什么区别?”_主线程面试官:“Redis的keys和scan命令有什么区别?”_主线程


前言

         有时,我们需要针对符合条件的一部分key进行操作,比如删除以test_开头的key

 ,当我们想利用通配符规则从Redis中查找匹配的key的时候,我们一般有2个命令可以选择,分别是keys和scan,接下来我们会详细说明它们之间的区别。




Keys命令


       当 KEYS 命令被用于处理一个大的数据库时, 又或者 SMEMBERS 命令被用于处理一个大的集合键时,它们会锁定Redis库, 可能会阻塞服务器达数秒之久,并增加redis 的CPU 占用,该命令的时间复杂度为O(N)。在Redis拥有数百万及以上的key的时候,这个命令执行的会比较慢,更为致命的是,这个命令会阻塞Redis多路复用的IO主线程,如果这个线程阻塞,在该命令执行期间,其他的发送向Redis服务端的命令,都会被阻塞,在高并发下会导致请求大量堆积进而导致服务雪崩。有些公司在生产环境直接禁用keys 命令。但是在Redis服务器key的数量不大的情况下,使用keys命令也是没啥问题的。

       如下图所示,keys命令的原理就是扫描整个Redis里面所有的key数据,然后根据我们的通配的字符串进行模糊查找出来

面试官:“Redis的keys和scan命令有什么区别?”_主线程_04






Scan命令


        基于上面的Keys命令的缺陷,我们这里再引入一个命令,代替keys命令,同样是O(N)复杂度的scan命令,支持通配查找,scan命令或者其他的scan如sscan ,HSCAN,ZSCAN命令,可以不用阻塞主线程,并支持游标按批次迭代返回数据,所以是比较理想的选择。(PS:scan命令就是分段扫描Redis库,一部分一部分地来扫描,而不是一次性扫描整个库,而且scan命令不阻塞主线程)keys相比scan命令优点是,keys是一次返回,而scan是需要迭代多次返回。

       如下图所示,scan命令的底层逻辑是分批次扫描,比如一次性扫描3个(这个值可以让我们自定义配置),依次顺序的扫描,直到扫完整个库为止  


面试官:“Redis的keys和scan命令有什么区别?”_主线程_05


SCAN的缺点:


  • 因为是分段获取key,所以它会多次请求redis服务器,这样势必会造成  扫描同样数目的key,scan耗时更长。

  • 在对键进行增量式迭代的过程中, 键可能会被修改, 所以增量式迭代命令只能对被返回的元素提供有限的保证。所以,在查询间隔中新增的key,不一定会被返回。

  • 另外,因为新增或删除key都会改变redis key的索引,所以,多次查询也会有重复的元素出现,所以使用scan命令,一定需要保证业务处理可重复执行



示例关键代码如下:




















long start = System.currentTimeMillis();        //需要匹配的key        String patternKey = "JavaAlliance:*";        ScanOptions options = ScanOptions.scanOptions()                //这里指定每次扫描key的数量                .count(2000)                .match(patternKey).build();        RedisSerializer<String> redisSerializer = (RedisSerializer<String>) redisTemplate.getKeySerializer();        Cursor cursor = (Cursor) redisTemplate.executeWithStickyConnection(redisConnection -> new ConvertingCursor<>(redisConnection.scan(options), redisSerializer::deserialize));        List<String> result = new ArrayList<>();        while(cursor.hasNext()){            result.add(cursor.next().toString());        }        //切记这里一定要关闭,否则会耗尽连接数。报Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisException: Could not get a         cursor.close();        log.info("scan扫描共耗时:{} ms key数量:{}",System.currentTimeMillis()-start,result.size());




总结


  1. Keys命令会锁住整个Redis库,会阻塞 Redis的IO多路复用主线程,可能会引起“服务雪崩”问题,但是并不会返回重复key。
  2.  Scan命令是分批次进行查询,不会锁住整个Redis库,不阻塞Redis的IO多路复用主线程。查询同样数量的key,会比Keys命令更耗时。在对键进行增量式迭代的过程中, 键可能会被修改, 所以增量式迭代命令只能对被返回的元素提供有限的保证。因为新增或删除key都会改变redis key的索引,所以,多次查询也会有重复的元素出现,故而应用程序处理的时候记得做幂等性校验处理



行百里者半九十,你知道的越多,你不知道的越多!拜拜!,下期再见!​面试官:“Redis的keys和scan命令有什么区别?”_主线程_06​​面试官:“Redis的keys和scan命令有什么区别?”_主线程_06面试官:“Redis的keys和scan命令有什么区别?”_主线程_06