「本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰!」html
redis的整數集是什麼?當咱們想set集合中添加整數時內部又是什麼結構?整數集默認是多少範圍?超出了範圍的數據是如何存儲的?刪除最長元素後會不會發生降級的變化? 今天,咱們就來對整數集一探究竟前端
爲何整數集升級後不能在進行降級操做 | intset位升級頻率java
往期回顧
【redis前傳】redis字典快速映射+hash釜底抽薪 | 單線程不影響後臺工做之漸進式rehashredis
【redis正傳】redis淘汰+過時雙向保證高可用 | 單線程如何作到快速響應後端
【redis前傳】 redis五大天王值list基本數據如何成長 | 由內以外深刻學習數組
【redis前傳】本身手寫一個LRU策略 | 抓住時間的尾巴markdown
基於redis實現分佈式鎖數據結構
【redis前傳】zset如何解決內部鏈表查找效率低下|跳錶構建app
前言
整數集合相信有的同窗沒有據說過,由於redis對外提供的只有封裝的五大對象!而咱們本系列主旨是學習redis內部結構。內部結構是redis五大結構重要支撐!分佈式
前面咱們分別從redis內部結構分析了redis的List、Hash、Zset三種數據結構了。今天咱們再來分析set數據結構內部是如何存儲的
基本結構
- 由此咱們可知在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中的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位。他的取值範圍是
−27∼27−1即−128∼127
添加元素
sadd juejin -123
sadd juejin -6
sadd juejin 12
sadd juejin 56
sadd juejin 321
複製代碼
-
上面咱們添加了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區間。
- 最後將新加入的元素填充進去。當發生升級時確定是由於新元素的長度大於原有長度了。那麼他的值必定會是在新數組的兩端。負數在最左側,正數在最右側
降級
- 接下來就是第二個問題當新加入的65535又被刪除了redis該怎麼辦,這個時候元素長度實際16位就能夠知足了,可是此時encoding倒是32位的。按照個人見解應該在實現降級!
- 可是遺憾的是redis並無,那麼請思考爲何沒有?若是讓你實現你將如何實現
爲何不實現降級
- 當加入元素超過當前長度咱們很容易就知道此時須要進行升級操做,可是當咱們刪除一個數據時咱們如何判斷是否須要降級卻很困難,咱們須要從新遍歷一遍剩下的元素是否小於當前長度,實現複雜度O(N) 。這就是爲何不進行降級緣由之一
- 你可能會說從新遍歷一遍很快的反正在內存中,那麼你有沒有想過若是降級以後又遇到升級狀況,這樣來回的升級降級就下降了咱們程序的性能了。咱們知道升級是必須的因此這裏降級redis採起的是忽略的策略
小結
參考資料:內存升級優化內存降級