redis—— 渐进式遍历

目录

为啥不用keys*遍历? 

 引入渐进式遍历

SCAN进行渐进式遍历

格式及参数说明

使用示例 

注意


 

为啥不用keys*遍历? 

之前学过key*  获取所有的key,但是这个操作可能会一次性得到太多的key,阻塞redis服务器,所以不建议在生产环境中使用。

  1. 性能问题:KEYS * 命令需要遍历整个数据库中的所有键名,这对于大型的 Redis 数据库来说会非常耗时和资源消耗。

  2. 阻塞问题:KEYS * 命令会阻塞 Redis 服务器的主线程,这意味着在执行这个命令期间,Redis 将无法处理其他客户端的请求。

  3. 安全问题:如果在生产环境中误操作使用 KEYS * 命令,可能会导致非常严重的安全问题。例如,可能会返回包含敏感信息的键名,或者删除一些重要的键值对

 引入渐进式遍历

Redis渐进式遍历允许我们在不阻塞Redis服务器的情况下,逐步获取大量的数据。所谓渐进式,不是一次性把所有Key拿到,而是每当执行一次命令,只获取到其中的一部分(多次迭代,逐步获取)。这样保证当前这一次操作不会太卡。多执行几次渐进式遍历,就可以得到所有的key,相当于化整为零

SCAN进行渐进式遍历

在Redis中,可以使用SCAN命令进行渐进式遍历。SCAN命令通过游标来记录当前遍历的位置,并返回一批键值对。我们可以使用返回的游标继续下一次遍历,直到完成遍历为止。

格式及参数说明

SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
#pattern等同于key中的pattern通配符
#count限制这一次遍历能够获取到多少元素,默认是10;与mysql中的limit不同;
#limit精确,redis对于count只是一个建议、提示,并且它和最后的结果也比一定一致
  • cursor 表示游标,用来记录当前遍历的位置,不能理解为下标并不是一个连续递增的整数,仅仅是一个字符串,供服务器使用;MATCH 参数表示要匹配的键名模式;
  • COUNT 参数表示每次遍历返回的最大元素个数,所以最后返回的结果条数并不一定与count值相等。COUNT这个里的数字,不是说每次遍历都得设置成一样。这
  • 其中执行命令的前半部分:下次遍历光标开始的位置.(这与传统的下标不同);后半部分:真正遍历到的key内容;
  • 当命令返回的cursor回到0了,才说明遍历结束了。

 

使用示例 

# 遍历整个数据库
SCAN 0
# 输出结果为:(10086, [key1, key2, key3, ...])

# 遍历数据库中匹配指定模式的键
SCAN 0 MATCH "prefix:*"
# 输出结果为:(10086, [prefix:key1, prefix:key2, prefix:key3, ...])

# 分批遍历数据库中的键
SCAN 0 COUNT 10
# 输出结果为:(10086, [key1, key2, key3, ..., key10])
#使用SCAN命令从游标0开始,每次返回最多10个键值对。
#如果还有更多的数据需要遍历,返回的结果中会包含一个新的游标值,我们可以使用这个新的游标值进行下一次遍历。
# 第一次执行scan 0
127.0.0.1:6379> scan 0
1) "11"
2)  1) "user"
    2) "a"
    3) "test"
    4) "user.name"
    5) "hk"
    6) "y"
    7) "user.city"
    8) "b"
    9) "list1"
   10) "c"
   
# 使用新的cursor = '11',执行scan 11
127.0.0.1:6379> scan 11
1) "0"
2) 1) "list2"
   2) "k"
   3) "x"

运行命令和结果解释:

1、第一次执行scan 0 ,返回结果包括两部分,第一部分 11 就是下次执行scan命令需要的cursor参数,第二部分是返回的10个键。

2、第二次执行scan 11 ,得到的结果是 “0”,说明所有的键都已经被遍历过了。

如果还有更多的数据需要遍历,返回的结果中会包含一个新的游标值,我们可以使用这个新的游标值进行下一次遍历。

需要注意,  由于Redis是一个内存数据库,如果要遍历的数据集比较大,可能会对服务器性能产生一定的影响。此外,由于Redis是一个键值存储数据库,遍历的顺序是不确定的。

总结一下,Redis的渐进式遍历通过SCAN命令实现,可以高效地逐步获取大量的数据。在实际使用中,我们可以根据需求设置合适的COUNT参数来控制每次返回的数据量,以及根据返回结果中的游标值来进行下一次遍历。每次使用 SCAN 命令遍历时,都需要将上一次遍历的游标作为下一次遍历的参数传递进去,以便继续从上次遍历结束的地方开始。因此,使用 SCAN 命令进行渐进式遍历时需要编写一些代码来管理游标。

注意事项

  • 渐进式遍历在遍历过程中,不会在服务器存储任何的状态信息,此处的遍历随时可以终止。

举个例子来理解: 

比如,我去买煮馍吃,商家都做了一半了,此时我想取消。

在这个例子中,我相当于客户端,商家相当于服务器, 如果我想取消,此时已经在服务器保留了状态信息,此时就会对服务器的运行造成影响。但是redis的服务器不保留任何状态

  • scan命令能有效的解决keys命令带来的阻塞问题,但是却带来新的问题。

    在scan过程中,如果键发生了变化(增、删、改),那么有可能会出现新增的键没有遍历到或者遍历出了重复键的情况。这是在开发过程中需要注意到的地方。

     

    使用迭代器或foreach循环遍历集合时,如果在遍历过程中直接调用集合的修改方法(如add、remove等),会导致迭代器的内部状态与集合的实际状态不一致,从而抛出ConcurrentModificationException异常。 

     

 

 

 

你可能感兴趣的:(Java,redis,java,redis,scan)