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