`
m635674608
  • 浏览: 4930283 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

RedisCluster的scan命令

 
阅读更多
在redis中是支持使用通配符的使用,例如‘?’或是’’,所以我们在获取redis里面的某个db里面的所有数据可以用 `keys `这样的指令来实现。但是存在一个问题就是这样做的话,在数据量很大的情况下效率是很不理想的,一般情况下redis的slowlog中总会少不了keys xxx这种类型操作(如果有人在其上面执行该操作的话)
 
scan命令可以帮助我们解决使用keys命令遍历大量数据而导致服务器阻塞的情况,它每次都只遍历一小部分数据,每次操作对应的时间复杂度为O(1)。
 
scan命令是一个基于游标(Cursor)的迭代器,scan命令每次被调用之后,都会向用户返回一个新的游标,用户在下次迭代时需要使用这个新游标作为scan命令的游标参数,延续之前的迭代过程。当scan命令的游标参数被设置成0时,服务器将会开始一次新的迭代,当服务器向用户返回的游标值为0时,表示迭代已经结束。
 
但对于RedisCluster来说,是不可以对所有键进行scan操作的,但可以针对其他数据类型,比如hash, zset,进行一系列hscan,zscan操作。其实可以从jedisCluster的实现可以看出,如果要对所有key进行scan,需要实现MultiKeyCommands,但RedisCluster是不支持这类型操作的,同理pipeline,mget,mset等操作。
 
但在redisCluster中可以通过getClusterNodes获取每个节点的连接,依次进行遍历并查询:
 
 
Java代码  收藏代码
  1. val jedisCluster = new JedisCluster(hostPorts)  
  2.    
  3.     val clusterNodes = jedisCluster.getClusterNodes.values()  
  4.     for (clusterNode: JedisPool <- clusterNodes) {  
  5.       var scanCursor = "0"  
  6.       val resource: Jedis = clusterNode.getResource  
  
 
在每个节点上执行scan命令,scan命令的结束条件并不是只有返回下个index=0,也有可能得到的列表为空,此时同样宣告没有新的key了。
 
此时就可以通过一个do/while循环来进行scan操作:
 
Java代码  收藏代码
  1. for (clusterNode: JedisPool <- clusterNodes) {  
  2.       var scanCursor = "0"  
  3.       var scanResult: util.List[String] = new util.ArrayList[String]()  
  4.       do {  
  5.         val resource: Jedis = clusterNode.getResource  
  6.         val scan: ScanResult[String] = resource.scan(scanCursor, new ScanParams().`match`(“xxx*"))  
  7.         scanCursor = scan.getStringCursor  
  8.         scanResult = scan.getResult  
  9.         for (key: String <- scanResult) {  
  10.           //xxx  
  11.         }  
  12.         resource.close()  
  13.       } while (!scanCursor.equals("0") && !scanResult.isEmpty)  
  14.     }  
 
 
 
scan时可以设置count(数量)以及match(匹配模式),以便于按照条件进行过滤。
 
 
但需要统计一下通过scan方法执行出来的键值总结果是否与keys执行结果相同(在相同的运行条件下,redis不能随意增加key)。
 
经过我们的验证,在cluster模式下,将所有key放到set下(考虑到master/slave键重复,cluster模式下keys会忽略重复的键,但是每个redis连接进行scan却不会)是相同的:
 
Java代码  收藏代码
  1. val keys = new mutable.HashSet[String]()  
  2.     for (clusterNode: JedisPool <- clusterNodes) {  
  3.       var scanCursor = "0"  
  4.       var scanResult: util.List[String] = new util.ArrayList[String]()  
  5.       do {  
  6.         val resource: Jedis = clusterNode.getResource  
  7.         val scan: ScanResult[String] = resource.scan(scanCursor, new ScanParams().`match`(PROD_SKU_PRICE + "*"))  
  8.         scanCursor = scan.getStringCursor  
  9.         scanResult = scan.getResult  
  10.         for (key: String <- scanResult) {  
  11.           keys.add(key)  
  12.         }  
  13.         resource.close()  
  14.       } while (!scanCursor.equals("0") && !scanResult.isEmpty)  
  15.     }  
  16.     print (keys.size)  
 
 
 
经过验证,总数量是相同的,通过本次测试同时也发现,scan命令的count不会每次真的返回该数量的key值(尽管后续还存在),只会返回比count值小的键值数量。
 
但考虑到在RedisCluster模式下并不能应用scan命令,我们可以对该命令进行一定的改造,使其能够支持按照hash tag进行操作。由于有着同样hash tag的键都会存放在同一个redis节点上,此时仅针对RedisCluster中的单个redis连接操作即可,有点类似之前说过的读写分离方案: http://brandnewuser.iteye.com/blog/2315636 ,直接在之前的方案中,增加scan方法(继承的JedisCluster类中),由于该操作中没有对应的key,因此需要额外传递hashTag用来指定使用到哪个节点来处理:
 
Java代码  收藏代码
  1. public ScanResult<String> scan(final String hashTag, final String cursor, final ScanParams params) {  
  2.     ZhenQueryContextHolder.getInstance().setQueryContext(new ZhenQueryContext(OperationType.READ));  
  3.     return new ZhenJedisClusterCommand<ScanResult<String>>(connectionHandler, maxRedirections) {  
  4.    
  5.         @Override  
  6.         public ScanResult<String> execute(Jedis connection) {  
  7.             return connection.scan(cursor, params);  
  8.         }  
  9.     }.run(hashTag);  
  10. }  
 
 
参考: 
 
分享到:
评论

相关推荐

    Redis中Scan命令的踩坑实录

    1、原本以为自己对redis命令还蛮熟悉的,各种数据模型各种基于redis的骚操作。但是最近在使用redis的scan的命令式却踩了一个坑,顿时发觉自己原来对redis的游标理解的很有限。所以记录下这个踩坑的过程,背景如下: ...

    redis 用scan指令 代替keys指令(详解)

    众所周知,当redis中key数量越大,keys 命令执行越慢,而且最重要的会阻塞服务器,对单线程的redis来说,简直是灾难,终于找到了替代命令scan。  SCAN cursor [MATCH pattern] [COUNT count] SCAN 命令及其相关的 ...

    Redis中Scan命令的基本使用教程

    二是scan命令,以非阻塞的方式实现key值的查找,绝大多数情况下是可以替代keys命令的,可选性更强 以下写入100000条key***:value***格式的测试数据(ps:用pipline的话,1w一笔,每一笔在秒级完成) # -*- coding: ...

    Redis中scan命令的深入讲解

    主要给大家介绍了关于Redis中scan命令的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用redis具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    Redis Scan命令的基本使用方法

    主要给大家介绍了关于Redis中Scan命令的基本使用方法,文中通过示例代码介绍的非常详细,对大家学习或者使用Redis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

    redis-scanstreams:Redis *SCAN 命令的流接口,用于 node_redis

    为 Redis *SCAN 命令提供流接口。 使用流版本客户端上的 SCAN、SSCAN、HSCAN 和 ZSCAN 方法。 您可以阅读有关 SCAN 的更多信息。 示例:与使用 var redis = require ( "redis" ) // replace the methods for any...

    redis中scan命令的基本实现方法

    redis的键在键值对大小大于hash-max-ziplist-value且个数小于hash-max-ziplist-entries的时候,是存放在散列表数据结构中的,在运行keys命令的时候,需要遍历数据库键空间,把所有键都取出来后与keys后面的pattern...

    php redis扩展支持scan命令实现方法

    主要介绍了php redis扩展支持scan命令实现方法的相关资料,需要的朋友可以参考下

    详解Redis SCAN命令实现有限保证的原理

    主要介绍了Redis SCAN命令实现有限保证的原理,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值 ,需要的朋友可以参考下

    Java代码中Redis的scan方法中cursor(即scanResult.getStringCursor())返回乱码

    Java代码中Redis的scan方法中cursor(即scanResult.getStringCursor())返回乱码错误信息遍历问题scan用法Java代码及问题错误原因新的问题 错误信息 新学redis,某天用scan操作做个删除某些键的小测试,碰到如下...

    用Redis实现分布式锁_redis_分布式_

    用Redis实现分布式锁的简易教程,在ppt中附有代码解析

    Redis获取某个前缀的key脚本实例

    SCAN命令的有SCAN,SSCAN,HSCAN,ZSCAN。 SCAN的话就是遍历所有的keys 其他的SCAN命令的话是SCAN选中的集合。 SCAN命令是增量的循环,每次调用只会返回一小部分的元素。所以不会有KEYS命令的坑。 SCAN命令返回...

    C#操作Redis服务器Demo

    C#操作Redis服务器Demo

Global site tag (gtag.js) - Google Analytics