共計 2308 個字符,預計需要花費 6 分鐘才能閱讀完成。
這篇文章主要為大家展示了“redis 整數集不能降級的原因是什么”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓丸趣 TV 小編帶領大家一起研究并學習一下“redis 整數集不能降級的原因是什么”這篇文章吧。
基本結構
在 src/t_set.c 中我們發現這樣一段代碼
由此我們可知在 set 中是由兩種數據結構構成的:hashtable+intset。關于 redis 內部其他的結構我專門在【redis 專欄中有介紹】。hashtable 不是我們今天的主角,我們今天先分析 intset 俗稱整數集合。
從上圖中我們可以看出,我構造了兩個 set 集合分別為【commonset】、【cs】。兩個集合前者存儲字符串、后者專門存儲數字。
我們在通過 object encoding key 來查看下兩個集合的底層數據結構,發現一個是 hashtable 一個是 intset 。這也驗證了我們上面對 set 基本結構的描述。
在 redis 中對外提供五大類型實際上都是 redis 的一個抽象對象叫做 redisobject。在內部映射了我們 redis 內部的數據結構
針對 commonset 和 cs 兩個集合在內部數據結構大概可以這么理解
何時使用 intset
你可以單純的認為只要是數字就會使用 intset 結構來存儲,我恐怕要給你當頭一棒了。實際上并不是這樣
需要同時滿足以下兩個條件:
intset
圖中表示的很清楚了,在 intset 中的 encoding 有三種取值分別代表 contents 保存數據類型。這里有人可能會有疑問了 contents 的類型不就是 int8_t 嗎?為什么還需要 encoding 呢?這里通過源碼跟蹤內部的確跟 int8_t 沒啥關系。而且數據的默認類型就是 int16_t。關于 length 這里無需太多解釋,記住一點表示 contents 元素的個數并非表示 contents 數組的長度!
了解 intset 的同學都知道在 encoding 三種取值范圍中涉及了升級的操作!在講升級之前我們先來了解下 C、C++ 中 int 的取值范圍是如何定義的
int8_t 的取值范圍是【-128,127】。類似于 java 中 byte 占 1 個字節也就是 8 位。他的取值范圍是
\[-2^{7} \sim 2^{7}-1 \\ 即 \\
-128 \sim 127
\]
添加元素
sadd juejin -123
sadd juejin -6
sadd juejin 12
sadd juejin 56
sadd juejin 321
juejin 這個 key 內部就是 intset。
上面我們添加了 5 個元素且這五個元素的長度都在 16 之內!所以當前的 intset 的 encoding=INTSET_ENC_INT16。-123 在 contents 中占前 16 位。
所以當前五個元素占 contents 的長度是 16*5=80;
注意 set 在存儲 int 類型數據時,內部是按照從小到大的順序存儲的。
類型變動
上面的問題不知道你有沒有考慮過,或者說有沒有遇到過!intset 默認是 int16 位,正如我們上面添加的五個元素。加入此時我們添加第 6 個元素是 65535(32 位)。那么此時 16 位的長度就不夠存儲了這個時候 intset 會怎么做!
另外當我們添加第 6 個元素后又將 65535 刪除了之后,結構和添加之前是否一樣!下面我們帶著這兩個問題來一探究竟!!!
升級
首先我們針對第一問題來看看。原來五個元素都是 16 位就可以滿足了,這個時候添加的 65535 是 32 位長度的。那么是不是可以直接追加 32 位分配給 65535 呢?
答案是肯定不行,首先直接追加無法保證數組元素的大小順序!其次如果前五個分別是 16 位,第 6 個是 32 位那么在 intset 結構中沒有多余的字段來進行標記。也就是說在解析的時候就無法判斷應該解析 16 位還是 32 位了.
redis 為了方便解析所以在有高長度加入時會將整個 contents 進行升級。意思就是將整個 contents 先進行擴容,然后在重新填充數據
加入 65535
首先根據 length 可以確定擴容后元素個數為 6,每個占位 32,所以 contents 長度為 32*6=192。此時前 80 位內容保持不變
舊數據移位
開辟了足夠的空間后,我們就可以對舊數據進行移位了這里我們從原數組的末尾開始移動,在移動之前需要明確在新數組中的排序位置。
此時我們首先將 321 進行比對確定在新數組中他的排名是第五名,那么他將占用新 contents 中 128~159 區間。
最終前 5 個元素就會被移動好。
最后將新加入的元素填充進去。當發生升級時肯定是因為新元素的長度大于原有長度了。那么他的值一定會是在新數組的兩端。負數在最左側,正數在最右側
降級
接下來就是第二個問題當新加入的 65535 又被刪除了 redis 該怎么辦,這個時候元素長度實際 16 位就可以滿足了,但是此時 encoding 卻是 32 位的。按照我的看法應該在實現降級!
但是遺憾的是 redis 并沒有,那么請思考為什么沒有?如果讓你實現你將如何實現
為什么不實現降級
當加入元素超過當前長度我們很容易就知道此時需要進行升級操作,但是當我們刪除一個數據時我們如何判斷是否需要降級卻很困難,我們需要重新遍歷一遍剩下的元素是否小于當前長度,實現復雜度 O(N)。這就是為什么不進行降級原因之一
你可能會說重新遍歷一遍很快的反正在內存中,那么你有沒有想過如果降級之后又遇到升級情況,這樣來回的升級降級就降低了我們程序的性能了。我們知道升級是必須的所以這里降級 redis 采取的是忽略的策略
小結
以上是“redis 整數集不能降級的原因是什么”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注丸趣 TV 行業資訊頻道!