hello,大家好,我是小慕,今天我们一起继续学习Redis的知识吧!
前言
有时,我们需要针对符合条件的一部分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数据,然后根据我们的通配的字符串进行模糊查找出来
Scan命令
基于上面的Keys命令的缺陷,我们这里再引入一个命令,代替keys命令,同样是O(N)复杂度的scan命令,支持通配查找,scan命令或者其他的scan如sscan ,HSCAN,ZSCAN命令,可以不用阻塞主线程,并支持游标按批次迭代返回数据,所以是比较理想的选择。(PS:scan命令就是分段扫描Redis库,一部分一部分地来扫描,而不是一次性扫描整个库,而且scan命令不阻塞主线程)keys相比scan命令优点是,keys是一次返回,而scan是需要迭代多次返回。
如下图所示,scan命令的底层逻辑是分批次扫描,比如一次性扫描3个(这个值可以让我们自定义配置),依次顺序的扫描,直到扫完整个库为止
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());
总结
- Keys命令会锁住整个Redis库,会阻塞 Redis的IO多路复用主线程,可能会引起“服务雪崩”问题,但是并不会返回重复key。
- Scan命令是分批次进行查询,不会锁住整个Redis库,不阻塞Redis的IO多路复用主线程。查询同样数量的key,会比Keys命令更耗时。在对键进行增量式迭代的过程中, 键可能会被修改, 所以增量式迭代命令只能对被返回的元素提供有限的保证。因为新增或删除key都会改变redis key的索引,所以,多次查询也会有重复的元素出现,故而应用程序处理的时候记得做幂等性校验处理
行百里者半九十,你知道的越多,你不知道的越多!拜拜!,下期再见!