redis中获取所有key值的方式有两种,一种是keys方式,另外一个方式,是使用迭代器的方式Scan。
在key值很多的情况下,如果使用keys,就有可能发生阻塞,因为redis是单线程的。
keys命令的时间复杂度是O(N),是遍历算法,会容易导致redis的服务卡顿。
Scan的时间复杂度同样也是O(N),但是scan是分次进行的,不会阻塞线程,并且提供了limit参数,可以控制每次返回结果的最大条数
redis的结构是使用了Hash表作为底层实现的,原因不外乎高效而且实现简单。redis的底层key的存储结构就是类似于HashMap那样的数组+链表的结构。其中第一维数组的大小为2n(n>=0),每次扩容数组长度就会扩大一倍。
scan命令就是对这个一维数组进行遍历。每次返回的游标值也都是这个数组的索引。limit参数表示遍历多少个数组的元素。将这些元素下挂接的符合条件的结果都返回。因为每个元素下挂接的链表大小不同,所以每次返回的结果数量也不同。
go的代码实现:
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
"time"
)
var rdb *redis.Client
func initRedis() (err error) {
ctx,cancel := context.WithTimeout(context.Background(),time.Second*10)
defer cancel()
rdb = redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
Password: "",
PoolSize: 200,
})
_,err = rdb.Ping(ctx).Result()
if err !=nil{
fmt.Println("ping redis failed err:",err)
return err
}
return nil
}
func main() {
err := initRedis()
if err !=nil{
fmt.Println("init redis failed err :",err)
return
}
ctx := context.Background()
var cursor uint64
keys,cursor,err := rdb.Scan(ctx,cursor,"*",100).Result()
if err !=nil{
fmt.Println("scan keys failed err:",err)
return
}
for _,key := range keys{
//fmt.Println("key:",key)
sType,err := rdb.Type(ctx,key).Result()
if err !=nil{
fmt.Println("get type failed :",err)
return
}
fmt.Printf("key :%v ,type is %v\n",key,sType)
if sType == "string" {
val,err := rdb.Get(ctx,key).Result()
if err != nil{
fmt.Println("get key values failed err:",err)
return
}
fmt.Printf("key :%v ,value :%v\n",key,val)
}else if sType == "list"{
val,err := rdb.LPop(ctx,key).Result()
if err !=nil{
fmt.Println("get list value failed :",err)
return
}
fmt.Printf("key:%v value:%v\n",key,val)
}
}
}