Redis中有一個經典的問題,在巨大的數據量的情況下,做類似於查找符合某種規則的Key的信息,這里就有兩種方式,
一是keys命令,簡單粗暴,由於Redis單線程這一特性,keys命令是以阻塞的方式執行的,keys是以遍歷的方式實現的復雜度是 O(n),Redis庫中的key越多,查找實現代價越大,產生的阻塞時間越長。
二是scan命令,以非阻塞的方式實現key值的查找,絕大多數情況下是可以替代keys命令的,可選性更強
以下寫入100000條key***:value***格式的測試數據(ps:用pipline的話,1w一筆,每一筆在秒級完成)
# -*- coding: utf-8 -*-
# !/usr/bin/env python3
import redis
import sys
import datetime
def create_testdata():
r = redis.StrictRedis(host='***.***.***.***', port=***, db=0, password='***')
counter = 0
with r.pipeline(transaction=False) as p:
for i in range(0, 100000):
p.set('key' + str(i), "value" + str(i))
counter = counter + 1
if (counter == 10000):
p.execute()
counter = 0
print("set by pipline loop")
if __name__ == "__main__":
create_testdata()
比如這里查詢key111開頭的key有哪些?
若使用keys命令,則執行keys key1111*,一次性全部查出來。
同樣,如果使用scan命令,則用 scan 0 match key1111* count 20
scan的語法為:
SCAN cursor [MATCH pattern] [COUNT count] The default COUNT value is 10.
SCAN命令是一個基於游標的迭代器。這意味着命令每次被調用都需要使用上一次這個調用返回的游標作為該次調用的游標參數,以此來延續之前的迭代過程。
這里使用scan 0 match key1111* count 20命令來完成這個查詢,稍顯意外的是,使用一開始都沒有查詢到結果,這個要從scan命令的原理來看。
scan在遍歷key的時候,0就代表第一次,key1111*代表按照key1111開頭的模式匹配,count 20中的20並不是代表輸出符合條件的key,而是限定服務器單次遍歷的字典槽位數量(約等於)。
那么,什么又叫做槽的數據?這個槽是不是Redis集群中的slot?答案是否定的。其實上圖已經給出了答案了。
如果上面說的“字典槽”的數量是集群中的slot,又知道集群中的slot數量是16384(RedisCluster設計有16384個槽),那么遍歷16384個槽之后,必然能遍歷出來所有的key信息,
上面清楚地看到,當遍歷的字典槽的數量20000的時候,游標依舊沒有走完遍歷結果,因此這個字典槽並不等於集群中的slot的概念。
經過測試,在scan的時候,究竟遍歷多大的COUNT值能完全match到符合條件的key,跟具體對象的key的個數有關,
如果以超過key個數的count來scan,必定會一次性就查找到所有符合條件的key,比如在key個數為10W個的情況下,一次遍歷20w個字典槽,肯定能完全遍歷出來結果。
scan 指令是一系列指令,除了可以遍歷所有的 key 之外,還可以對指定的容器集合進行遍歷。
zscan 遍歷 zset 集合元素,
hscan 遍歷 hash 字典的元素、
sscan 遍歷 set 集合的元素。
SSCAN 命令、 HSCAN 命令和 ZSCAN 命令的第一個參數總是一個數據庫鍵(某個指定的key)。
另外,使用redis desktop manager的時候,當刷新某個庫的時候,控制台自動不斷刷新scan命令,也就知道它在干嘛了
scan命令的特點: 鄭州較好的婦科醫院 http://www.kd0371.com/
1、復雜度雖然也是 O(n),但是它是通過游標分步進行的,不會阻塞線程;
2、提供 limit 參數,可以控制每次返回結果的最大條數,limit 只是一個 hint,返回的結果可多可少;
3、同 keys 一樣,它也提供模式匹配功能;
4、服務器不需要為游標保存狀態,游標的唯一狀態就是 scan 返回給客戶端的游標整數;
5、返回的結果可能會有重復,需要客戶端去重復,這點非常重要;
6、遍歷的過程中如果有數據修改,改動后的數據能不能遍歷到是不確定的;
7、單次返回的結果是空的並不意味着遍歷結束,而要看返回的游標值是否為零