Redis scan命令原理

2021-09-12 17:09:31 字數 3496 閱讀 2842

scan cursor [match pattern] [count count]

sscan key cursor [match pattern] [count count]

hscan key cursor [match pattern] [count count]

zscan key cursor [match pattern] [count count]

scan:迭代當前庫

sscan:迭代乙個 set 型別

hscan:迭代乙個hash型別,並返回相應的值

zscan:迭代乙個sorted set,並且返回相應的分數

redis是單程序單執行緒模型,keys和smembers這種命令可能會阻塞伺服器,所以出現了scan系列的命令,通過返回乙個游標,可以增量式迭代.

scan,sscan,hscan,zsan分別有自己的命令入口,入口中會進行引數檢測和游標賦值,然後進入統一的入口函式:scangenericcommand,以hscan命令為例:

scangenericcommand主要分四步:

游標在儲存為hash的時候發揮作用,具體入口函式為dictscan,下文詳細描述。

返回結果到客戶端,是乙個陣列,第乙個值是游標,第二個值是具體的鍵值對

當迭代乙個雜湊表時,存在三種情況:

redis中進行rehash時會存在兩個雜湊表,ht[0]與ht[1],並且是漸進式rehash(即不會一次性全部rehash);新的鍵值對會存放到ht[1]中並且會逐步將ht[0]的資料轉移到ht[1].全部rehash完畢後,ht[1]賦值給ht[0]然後清空ht[1].

因此游標的實現需要兼顧以上三種情況,以上三種情況的游標實現要求如下:

假設bucket0讀完之後返回了游標1,當客戶端再次帶著游標1返回時雜湊表已經進行完rehash,並且size擴大了一倍變成了8.redis按如下方法計算乙個鍵的bucket:

hash(key)&(size-1)
即如果size是4時,hash(key)&11,如果size是8時,hash(key)&111.因此當從4擴容到8時,原先在0bucket的資料會分散到0(000)與4(100)兩個bucket,bucket對應關係表如下:

從二進位制來看,當size為4時,hash(key)之後取低兩位即 hash(key)&11即key的bucket位置,如果size為8時,bucket位置為 hash(key)&111,即取低三位,當低兩位為00時,如果第三位為0,則為000,如果第三位為1,則為100,正好是4.其他槽位的類似.所以如果此時繼續按第一種方法遍歷,第四個bucket取到的值全部為重複值

所以為了兼顧以上三種情況,做到不漏資料並且盡量不重複,redis使用了一種叫做reverse binary iteration的方法.具體的游標計算**如下:

**邏輯很簡單,下面示例從4變為8和從4變為16以及從8變為4和從16變為4時,這種方法為何能夠做到不重不漏

遍歷size為4時的游標狀態轉移為0-2-1-3.

同理,size為8時的游標狀態轉移為0-4-2-6-1-5-3-7.

size為16時的游標狀態轉義為0-8-4-12-2-10-6-14-1-9-5-13-3-11-7-15

可以看出,當size由小變大時,所有原來的游標都能在大的hashtable中找到相應的位置,並且順序一致,不會重複讀取並且不會遺漏

例如size原來是4變為了8,且第二次遍歷時rehash已經完成.此時游標為2,根據圖2,我們知道size為4時的bucket2會rehash到size為8時的2和6.而size為4時的bucket0rehash到size為8時的0和4

由於bucket 0 已經遍歷完,也即size為8時的0,4已經遍歷,正好開始從2開始繼續遍歷,不重複也不會遺漏

繼續考慮size由大變小的情況.假設size由16變為了4,分兩種情況,一種是游標為0,2,1,3中的一種,此時繼續讀取,也不會遺漏和重複

但如果游標返回的不是這四種,例如返回了10,10&11之後變為了2,所以會從2開始繼續遍歷.但由於size為16時的bucket2已經讀取過,並且2,10,6,14都會rehash到size為4的bucket2,所以會造成重複讀取

size為16時的bucket2。即有重複但不會遺漏

總結一下:redis裡邊rehash從小到大時,scan系列命令不會重複也不會遺漏.而從大到小時,有可能會造成重複但不會遺漏.

截止目前,情況1和情況2已經比較完美的處理了。情況3看看如何處理

情況3需要從ht[0]和ht[1]中都取出資料,主要的難點在於如何在size大的雜湊表中找到應該取哪些bucket.redis**如下:

判斷條件為:

v&(m0^m1)
size 4的m0為00000011,size8的m1為00000111,二者異或之後取值為00000100,即取二者mask高位的值,然後&v,看游標是否在高位還有值

下乙個游標的取值方法為

v = (  ((v | m0) +1)& ~m0) | ( v & m0)
右半部分 取v的低位,左半部分取v的高位。 (v&m0)取出v的低位 例如size = 4時為 v&00000011

左半部分 (v|m0) + 1即將v的低位都置為1,然後+1之後會進製到v的高位,再次 & ~m0之後即取出了v的高位

整體來看每次將游標v的高位加1.下邊舉例來看:

假設游標返回了2,並且正在進行rehash,此時size由4變成了8 .則m0 = 00000011 v = 00000010

根據公式計算出的下乙個游標為 ( (( 00000010|00000011) +1 ) & (11111100) )| (00000010 & 00000011) = (00000100)&(11111100)|(00000010) = (00000110) 正好是6

判斷條件為 (00000010) & (00000011 ^ 00000111) = (00000010) & (00000100) = (00000000) 為0,結束迴圈

Redis scan命令原理

scan cursor match pattern count count sscan key cursor match pattern count count hscan key cursor match pattern count count zscan key cursor match pat...

Redis Scan迭代器遍歷操作原理(一)

redis在2.8.0版本新增了眾望所歸的scan操作,從此再也不用擔心敲入了keys 然後舉起雙手看著鍵盤等待漫長的系統卡死了 命令的官方介紹在這裡,中文版由huangz同學細心翻譯了,作者antirez的介紹在這裡 finally redis collections are iterable 我...

Ping命令原理

icmp是 internet control message protocol internet控制 報文協議。它是 tcp ip協議族 的乙個子協議,用於在ip主機 路由器之間傳遞控制訊息。控制訊息是指 網路通不通 主機是否可達 路由是否可用等網路本身的訊息。這些控制訊息雖然並不傳輸使用者資料,但...