《Redis學習整理--第三期Redis底層的數據結構》

本文章參考http://blog.csdn.net/caishenfans/article/details/44784131 

redis對象類型簡介
redis 是一種key/value型數據庫,每一個key和value都是使用對象表示的

redis有五種對象的類型


redis中一個對象的結構體表示如下




type表示該對象的對象類型 ,上面的五種的一個。但爲了提高存儲效率和程序的執行效率,每種對象的底層數據結構實現都可能不止一種,encoding就表示了對象底層所使用的編碼。

redis對象底層數據結構

底層數據結構共有八種


字符串對象
字符串對象的編碼可以是 int raw 或者embstr

字符串類型對象中的value值就是數字和字符串 ,如果一個字符串對象的內容可以轉換爲long。那麼該字符串就會被轉換成long類型,對象的ptr就會指向該long,並且對象的類型也用int表示。

普通的字符串有兩種,embstr和raw。embstr是redis3.0新增的數據結構,如果字符串的長度小於39字節(3.2之後改成了44),就用embstr。否則就使用傳統的raw對象。 redis中代碼:


embstr的好處:
embstr的創建只需分配一次內存。raw爲兩次(一次爲sds分配對象,另一次爲objet分配對象,embstr省去了第一次)
相對的,釋放內存的次數也有兩次變爲一次
embstr的objet和sds放在一起,更好的利用緩存帶來的優勢

需要注意的是redis沒有對embstr提供任何修改方式,所以embstr是隻讀的,對embstr的修改實際上是轉換爲raw後的修改

列表對象
列表對象的編碼可以是ziplist或者linkedlist。
ziplist是一種壓縮鏈表,它的好處是更能節省內存空間,因爲它所存儲的內容都是在連續的內存區域當中的。當列表對象元素不大,每個元素也不大的時候,就採用ziplist存儲。但當數據量過大時就ziplist就不是那麼好用了。因爲爲了保證他存儲內容在內存中的連續性,插入的複雜度是O(N),即每次插入都會重新進行realloc。如下圖所示,對象結構中ptr所指向的就是一個ziplist。整個ziplist只需要malloc一次,它們在內存中是一塊連續的區域。

linkedlist是一種雙向鏈表。它的結構比較簡單,節點中存放pre和next兩個指針,還有節點相關的信息。當每增加一個node的時候,就需要重新malloc一塊內存。

哈希對象
哈希對象的底層實現可以是ziplist或者hashtable

ziplist中的哈希對象是按照key1,value1,key2,value2這樣的順序存放來存儲的。當對象數目不多且內容不大時,這種方式效率是很高的。

hashtable的是由dict這個結構來實現的


dicht[0] 是用於真正存放數據,dicht[1]一般在哈希表元素過多進行rehash的時候用於中轉數據。
dictht中的table用語真正存放元素了,每個key/value對用一個dictEntry表示,放在dictEntry數組中



一個指向dictType結構的指針(type)。它通過自定義的方式使得dict的key和value能夠存儲任何類型的數據。

一個私有數據指針(privdata)。由調用者在創建dict的時候傳進來。

兩個哈希表(ht[2])。只有在重哈希的過程中,ht[0]和ht[1]才都有效。而在平常情況下,只有ht[0]有效,ht[1]裏面沒有任何數據。上圖表示的就是重哈希進行到中間某一步時的情況。

當前重哈希索引(rehashidx)。如果rehashidx = -1,表示當前沒有在重哈希過程中;否則,表示當前正在進行重哈希,且它的值記錄了當前重哈希進行到哪一步了。

當前正在進行遍歷的iterator的個數。這不是我們現在討論的重點,暫時忽略。

一個dictEntry指針數組(table)。key的哈希值最終映射到這個數組的某個位置上(對應一個bucket)。如果多個key映射到同一個位置,就發生了衝突,那麼就拉出一個dictEntry鏈表。

size:標識dictEntry指針數組的長度。它總是2的指數。

sizemask:用於將哈希值映射到table的位置索引。它的值等於(size-1),比如7, 15, 31, 63,等等,也就是用二進制表示的各個bit全1的數字。每個key先經過hashFunction計算得到一個哈希值,然後計算(哈希值 & sizemask)得到在table上的位置。相當於計算取餘(哈希值 % size)。

used:記錄dict中現有的數據個數。它與size的比值就是裝載因子(load factor)。這個比值越大,哈希值衝突概率越高

集合對象
集合對象的數據結構可以是intset或者hashtable
intset是一個整數集合,裏面存放的爲某種同一類型的整數,支持如下三種長度的整數

intset是一個有序集合,查找元素的複雜度爲O(logN),但插入時不一定爲O(logN),因爲有可能涉及到升級操作。比如當集合裏全是int16_t型的整數,這時要插入一個int32_t,那麼爲了維持集合中數據類型的一致,那麼所有的數據都會被轉換成int32_t類型,涉及到內存的重新分配,這時插入的複雜度就爲O(N)了。是intset不支持降級操作。

有序集合對象
有序集合的編碼可能兩種,一種是ziplist,另一種是skiplist與dict的結合。
ziplist作爲集合和作爲哈希對象是一樣的,member和score順序存放。按照score從小到大順序排列。它的結構不再複述。
skiplist是一種跳躍表,它實現了有序集合中的快速查找,在大多數情況下它的速度都可以和平衡樹差不多。但它的實現比較簡單,可以作爲平衡樹的替代品。它的結構比較特殊。下面分別是跳躍表skiplist和它內部的節點skiplistNode的結構體:

head和tail分別指向頭節點和尾節點,然後每個skiplistNode裏面的結構又是分層的(即level數組)
用圖表示,大概是下面這個樣子:

每一列都代表一個節點,保存了member和score,按score從小到大排序。每個節點有不同的層數,這個層數是在生成節點的時候隨機生成的數值。每一層都是一個指向後面某個節點的指針。這種結構使得跳躍表可以跨越很多節點來快速訪問。

前面說到了,有序集合ZSET是有跳躍表和hashtable共同形成的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章