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