日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Redis命令之scan、sscan、hscan、zcan

發布時間:2023/12/14 数据库 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis命令之scan、sscan、hscan、zcan 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

官方文檔:https://redis.io/commands/scan#the-count-option
與Keys、smembers的比較

  • keys命令以遍歷的方式迭代整個庫,實現的復雜度是 O(n),庫中的key越多,速度越慢,由于Redis的單線程處理,阻塞的時間也就越長。smembers也一樣,只不過它并不是阻塞整個庫,影響只針對單個set,當set過大時會存在同樣的問題。
  • scan命令的時間復雜度雖然也是O(N),但它是分次進行的,不會阻塞線程。
  • scan命令提供了limit參數,可以控制每次返回結果的最大條數。
  • scan命令返回的結果有可能重復,因此需要客戶端去重

SCAN語法

?

scan cursor [MATCH pattern] [COUNT count] #基于整個redis庫進行掃描 sscan key cursor [MATCH pattern] [COUNT count] #掃描指定的set類型的key hscan key cursor [MATCH pattern] [COUNT count] #掃描指定的hash類型的key zscan key cursor [MATCH pattern] [COUNT count] #掃描指定的zset類型的key

? 參數說明:

  • cursor表示本次掃描的開始游標,可以理解成從開始索引。
  • pattern表示正則表達式
  • count表示希望返回的個數,實際返回個數會圍繞該數波動,返回的個數可能等于該值也可能有一定出入,默認為10。

SCAN返回值
? ? ? ? scan返回值
是兩個值的數組:第一個值表示在下一個調用中使用的新游標,第二個值是本次迭代結果集。如果已經完成了一次完整的迭代,那么服務器會返回一個為0的游標,告訴客戶端,Redis已經對庫<或set/zset、hash>完成了一次完整的迭代。

192.168.192.128:6703> scan 0 MATCH user.* COUNT 5 1) "0" 2) 1) "user.stz" 192.168.192.128:6703> sadd setkey a01 a02 a03 a04 a05 a05 b02 b03 b04 b05 -> Redirected to slot [2440] located at 192.168.192.128:6701 (integer) 9 192.168.192.128:6701> sscan setkey 0 MATCH a* COUNT 2 1) "6" 2) 1) "a03" 192.168.192.128:6701> sscan setkey 6 MATCH a* COUNT 2 1) "1" 2) 1) "a02" 192.168.192.128:6701> sscan setkey 1 MATCH a* COUNT 2 1) "5" 2) 1) "a04"2) "a05" 192.168.192.128:6701> sscan setkey 5 MATCH a* COUNT 2 1) "0" 2) 1) "a01" 192.168.192.128:6701>

以下內容摘自:https://www.jianshu.com/p/be15dc89a3e8
SCAN的遍歷順序
? ??
關于scan命令的遍歷順序,我們可以用一個小栗子來具體看一下。

127.0.0.1:6379> keys * 1) "db_number" 2) "key1" 3) "myKey" 127.0.0.1:6379> scan 0 MATCH * COUNT 1 1) "2" 2) 1) "db_number" 127.0.0.1:6379> scan 2 MATCH * COUNT 1 1) "1" 2) 1) "myKey" 127.0.0.1:6379> scan 1 MATCH * COUNT 1 1) "3" 2) 1) "key1" 127.0.0.1:6379> scan 3 MATCH * COUNT 1 1) "0" 2) (empty list or set)

我們的Redis中有3個key,我們每次只遍歷一個一維數組中的元素。如上所示,SCAN命令的遍歷順序是
0->2->1->3,
這個順序看起來有些奇怪。我們把它轉換成二進制就好理解一些了。
00->10->01->11

我們發現每次這個序列是高位加1的。普通二進制的加法,是從右往左相加、進位。而這個序列是從左往右相加、進位的。這一點我們在redis的源碼中也得到印證。

在dict.c文件的dictScan函數中對游標進行了如下處理

v = rev(v); v++; v = rev(v);

意思是,將游標倒置,加一后,再倒置,也就是我們所說的“高位加1”的操作。

這里大家可能會有疑問了,為什么要使用這樣的順序進行遍歷,而不是用正常的0、1、2……這樣的順序呢,這是因為需要考慮遍歷時發生字典擴容與縮容的情況(不得不佩服開發者考慮問題的全面性)。

我們來看一下在SCAN遍歷過程中,發生擴容時,遍歷會如何進行。加入我們原始的數組有4個元素,也就是索引有兩位,這時需要把它擴充成3位,并進行rehash。

rehash

原來掛接在xx下的所有元素被分配到0xx和1xx下。在上圖中,當我們即將遍歷10時,dict進行了rehash,這時,scan命令會從010開始遍歷,而000和100(原00下掛接的元素)不會再被重復遍歷。

再來看看縮容的情況。假設dict從3位縮容到2位,當即將遍歷110時,dict發生了縮容,這時scan會遍歷10。這時010下掛接的元素會被重復遍歷,但010之前的元素都不會被重復遍歷了。所以,縮容時還是可能會有些重復元素出現的。

Redis的rehash

rehash是一個比較復雜的過程,為了不阻塞Redis的進程,它采用了一種漸進式的rehash的機制。

/* 字典 */ typedef struct dict {// 類型特定函數dictType *type;// 私有數據void *privdata;// 哈希表dictht ht[2];// rehash 索引// 當 rehash 不在進行時,值為 -1int rehashidx; /* rehashing not in progress if rehashidx == -1 */// 目前正在運行的安全迭代器的數量int iterators; /* number of iterators currently running */ } dict;

在Redis的字典結構中,有兩個hash表,一個新表,一個舊表。在rehash的過程中,redis將舊表中的元素逐步遷移到新表中,接下來我們看一下dict的rehash操作的源碼。

/* Performs N steps of incremental rehashing. Returns 1 if there are still* keys to move from the old to the new hash table, otherwise 0 is returned.** Note that a rehashing step consists in moving a bucket (that may have more* than one key as we use chaining) from the old to the new hash table, however* since part of the hash table may be composed of empty spaces, it is not* guaranteed that this function will rehash even a single bucket, since it* will visit at max N*10 empty buckets in total, otherwise the amount of* work it does would be unbound and the function may block for a long time. */ int dictRehash(dict *d, int n) {int empty_visits = n*10; /* Max number of empty buckets to visit. */if (!dictIsRehashing(d)) return 0;while(n-- && d->ht[0].used != 0) {dictEntry *de, *nextde;/* Note that rehashidx can't overflow as we are sure there are more* elements because ht[0].used != 0 */assert(d->ht[0].size > (unsigned long)d->rehashidx);while(d->ht[0].table[d->rehashidx] == NULL) {d->rehashidx++;if (--empty_visits == 0) return 1;}de = d->ht[0].table[d->rehashidx];/* Move all the keys in this bucket from the old to the new hash HT */while(de) {uint64_t h;nextde = de->next;/* Get the index in the new hash table */h = dictHashKey(d, de->key) & d->ht[1].sizemask;de->next = d->ht[1].table[h];d->ht[1].table[h] = de;d->ht[0].used--;d->ht[1].used++;de = nextde;}d->ht[0].table[d->rehashidx] = NULL;d->rehashidx++;}/* Check if we already rehashed the whole table... */if (d->ht[0].used == 0) {zfree(d->ht[0].table);d->ht[0] = d->ht[1];_dictReset(&d->ht[1]);d->rehashidx = -1;return 0;}/* More to rehash... */return 1; }

通過注釋我們就能了解到,rehash的過程是以bucket為基本單位進行遷移的。所謂的bucket其實就是我們前面所提到的一維數組的元素。每次遷移一個列表。下面來解釋一下這段代碼。

  • 首先判斷一下是否在進行rehash,如果是,則繼續進行;否則直接返回。
  • 接著就是分n步開始進行漸進式rehash。同時還判斷是否還有剩余元素,以保證安全性。
  • 在進行rehash之前,首先判斷要遷移的bucket是否越界。
  • 然后跳過空的bucket,這里有一個empty_visits變量,表示最大可訪問的空bucket的數量,這一變量主要是為了保證不過多的阻塞Redis。
  • 接下來就是元素的遷移,將當前bucket的全部元素進行rehash,并且更新兩張表中元素的數量。
  • 每次遷移完一個bucket,需要將舊表中的bucket指向NULL。
  • 最后判斷一下是否全部遷移完成,如果是,則收回空間,重置rehash索引,否則告訴調用方,仍有數據未遷移。

由于Redis使用的是漸進式rehash機制,因此,scan命令在需要同時掃描新表和舊表,將結果返回客戶端。

?

?

總結

以上是生活随笔為你收集整理的Redis命令之scan、sscan、hscan、zcan的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。