likes
comments
collection
share

redis系列——keys真的屡试不爽吗?

作者站长头像
站长
· 阅读数 26

keys

用过redis的宝子都知道,要从redis中根据条件查询特定前缀的key列表,可以这么做:

keys xxx*

简单暴力,爱咋咋地~

但是如果redis包含了200个w的key时,还能爱咋咋地吗?

首先,200w没有offset和limit,刷屏估计得刷好一会儿。。。

然后,keys算法是遍历算法,复杂度为O(n),如果是百万级以上的数量时,对于单线程的redis来说,一旦开始keys,其它请求都只能等(表面笑嘻嘻,心里MMP)。。。

所以,keys不是你想用就能用的。它的前置条件是key不多时。

scan

既然一屏到底的keys有严格的使用场景,那么有没替代的指令呢?有的,scan应运而生~

特点

相对keys,scan有以下几个特点:

  • 和keys一样,它也提供模式匹配功能。
  • 复杂度虽然也是O(n),但是增加了指定游标参数cursor和数目参数count,相当于增加了SQL查询的offset和limit。这样“分页查询“就实现了。这里需要注意的是,count并不是返回的条数,而是扫描的桶数。如下图的0、1、2、3...就是桶的下标。 redis系列——keys真的屡试不爽吗?
  • 返回的结果可能有重复,需要客户端进行去重处理。这里需要标红加粗。 scan在扫描数据时,相当于进行了分页处理,在分页读取的间隙,别的线程可能对扫描的键进行增删处理,这些处理会导致scan的cursor出现一定的偏差,导致扫描结果出现重复的键值对。应用程序需要特别处理这种情况。
  • 遍历的过程中如果有数据修改,修改后的数据能否遍历到是不确定的。 原因和上一点类似。
  • 单次返回结果为空并不意味着遍历结束,需要根据返回的cursor是否为0来确定是否遍历结束。

参考代码

package main

import (
    "github.com/go-redis/redis"
    "log"
)

func main() {
    // init redis manager
    mgr := redis.NewClient(&redis.Options{
       Addr: "127.0.0.1:6379",
    })

    if mgr == nil {
       log.Fatalf("redis.NewClient failed.")
       return
    }

    hkey := "hahatables"
    //// 插入hkey
    //keyNum := 100
    //for i := 0; i < keyNum; i++ {
    // cmd := mgr.HSet(hkey, strconv.Itoa(i), "value"+strconv.Itoa(i))
    // if cmd.Err() != nil {
    //    log.Fatalf("redis.HSet(%s) failed : %v", hkey, cmd.Err())
    //    return
    // }
    //}
    var (
       total     int
       keyResult []string
       subKey    = "7*"
       count     = int64(1000)
       cursor    uint64
       keys      []string
       err       error
    )

    cmd := mgr.HScan(hkey, cursor, subKey, count)
    for {
       if keys, cursor, err = cmd.Result(); err != nil {
          log.Fatalf("hscan cursor %v failed : %v", cursor, err)
          return
       }
       total += len(keys)
       keyResult = append(keyResult, keys...)
       if cursor > 0 { // 还有结果
          cmd = mgr.HScan(hkey, cursor, subKey, count)
       } else {
          break
       }
    }
    log.Printf("total : %v %v 7*s\n", total, keyResult)
}

这个例子中简单地测试了下hscan的使用,sscan、zscan功能类似。

总结

对于不确定key数量的查询,最好使用scan替换,避免出现因为keys指令导致redis卡顿的问题。

转载自:https://juejin.cn/post/7372734627163324426
评论
请登录