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) } } }