Redis資料型別底層資料結構
Redis目前基本的資料型別有String、List、Set、ZSet、Hash五種,首先Redis是C語言開發的,所以底層就是用C語言封裝資料結構或者C語言本身提供的資料結構來儲存。redis內部的主要資料結構主要有簡單字串(SDS)、雙端連結串列、字典、壓縮列表、跳躍表、整數集合。Redis內部並沒有直接使用這些資料結構來實現鍵值對資料庫,而是基於這些資料結構建立了一個物件系統,這個物件系統包含了我們所熟知的五種基本型別資料,也就是字串物件、列表物件、雜湊物件、集合物件和有序集合物件這五種型別的物件。而它們每一種物件都使用到了至少一種前面所介紹的資料結構。下面介紹一下redis內部的主要幾個資料結構簡單字串(SDS)、雙端連結串列、壓縮列表、跳躍表的定義。然後再介紹一下redis基本的五種資料型別,也就是五種型別的物件用到了上面的哪些資料結構。
redis的資料結構
SDS(Simple Dynamic String)簡單字串
1、redis定義:
2、使用範圍:在redis裡面,C本身的字串只會作為字串字面量(String literal)只用在一些不必對字串值修改的地方,比如列印日誌。
而redis需要使用字串儲存並且會修改的地方,都使用了SDS來儲存。例如Key值。
3、優點:使用SDS來儲存字串的優點:
- SDS的len屬性直接記錄了長度,獲取字串長度的複雜度為O(1)。
- C字串本身不記錄長度容易產生快取區溢位,而SDS杜絕了緩衝區的溢位。
- C字串本身不記錄長度,每次修改都要重新分配記憶體,SDS減少了重新分配記憶體次數。
- 優化了字串縮短操作。並且可以儲存任意格式的二進位制資料,而C字串必須含有編碼。
連結串列(list)
1、連結串列:listNode結構來儲存,多個listNode可以形成雙向連結串列,redis定義了list表示頭節點來持有連結串列,下圖分別是節點listNode和連結串列list的定義。
2、redis定義:
-
節點listNode
-
連結串列list
3、使用範圍:連結串列在redis中用作了列表鍵、釋出與訂閱、慢查詢、監視器等
跳躍表(zskiplist)
1、跳躍表:是一種有序得資料結構,通過在每個節點上維持多個指向其他節點的指標,從而達到快速訪問的目的,可以理解為改進版的雙端連結串列,改進的手段是通過空間換取了時間。
2、複雜度:跳躍表支援平均O(logN)、最壞O(N)的查詢複雜度,大部分條件下,跳躍表的效率可以和平衡樹媲美,並且實現比平衡樹簡單。
-
跳躍表節點zskiplistNode
-
跳躍表zskiplist
3、跳躍表結構圖:
仔細觀察上圖跳躍表的結構後,發現如果節點的層數越高,那麼這個節點訪問其他節點的速度就越快。換言之,level越高,代表了這個跳躍表的查詢效率可能會比較高。當然並不是絕對的,因為redis每次建立跳躍表節點時,程式是根據冪次定律(越大的數出現概率越小), 生成層數高度。同時,節點的順序是根據每個節點的分值排序的,如果分值相同,那麼根據物件的大小排序。
壓縮列表(ziplist)
1、壓縮列表:是redis為了節省記憶體而開發的,是由一系列特殊編碼的連續記憶體塊組成的順序型資料結構,一個壓縮列表的可以包含多個節點,每個節點可以儲存一個位元組陣列或者一個整數值。
2、壓縮列表結構圖:
3、壓縮列表特點:
- 是一種為節省記憶體開發的順序性資料結構
- 可以包含多個節點,每個節點儲存一個位元組陣列或者整數值
- 新增新節點到壓縮列表,或者從壓縮列表刪除節點,可能會引發連鎖更新操作,但是機率不高
Redis五種基本資料型別
上面提到過,redis並沒有使用上面的資料結構直接用來實現鍵值對資料庫,也就是常說的五種基本資料型別,而是建立了一個物件系統,這個系統包含了字串物件,列表物件、雜湊物件、集合物件、和有序集合物件這五種基本資料型別。這樣做有一個好處是,我們可以針對不同的場景,對相同的資料型別物件使用不同的資料結構,來優化提高效率。
redisObject物件
1、物件:redis的鍵值對都是一個redisObject結構,該結構中有三個屬性,type型別屬性、encoding編碼屬性、ptr指向底層資料結構屬性。
- redisObject物件定義
- 資料庫的key值都是一個string字串物件
2、編碼常量:
String型別
字串物件的編碼是int、raw、embstr。參考上面的編碼常量表,也就是說字串型別的資料底層的資料結構使用的是整數、SDS、embstr編碼的SDS。
1、編碼轉換
即上述幾種編碼會在何時轉換,也就是redis底層決定用什麼儲存字串資料?。
當int型別的編碼通過操作儲存的是字串值,那麼字串物件的編碼將從int變為raw。
List型別
列表物件的編碼可以是zipList壓縮列表和linkedlist雙端連結串列。
1、編碼轉換
即上述兩種編碼會在何時轉換,也就是redis底層什麼時候會用壓縮列表儲存列表資料?什麼時候會使用雙端連結串列儲存列表資料。
當列表同時滿足以下兩個條件時,列表物件會使用zipList編碼,也就是壓縮列表
- 列表物件儲存的所有字串元素的長度都小於64位元組
- 列表儲存的元素少於512個,
2、配置
上述兩個條件是支援配置的,也就是說我們可以redis直接讀取我們的配置,來決定列表list型別底層使用什麼樣的資料結構來儲存資料
- list-max-ziplist-value
- list-max-ziplist-entries
Set型別
集合物件使用的是intset整數集合(intset底層使用的是整數集合資料結構)或者hashtable雜湊表(hashtable底層使用的是字典資料結構,我們並沒有在本文做詳細介紹,有需要可以自己瞭解)
1、編碼轉換
當集合物件同時滿足下面兩個條件,會使用intset編碼
- 集合物件儲存的所有物件都是整數值
- 集合物件儲存的元素數量小於512個;
2、配置
上述第二個條件是支援配置的。
- set-max-intset-entries
ZSet型別
有序集合的編碼使用的是ziplist壓縮列表和skiplist跳躍表。
注意:上面介紹skiplist的時候我們可以從結構圖中明顯看到儲存集合元素的時候,score在每個節點中式如何儲存的。那麼如果ZSet使用的式ziplist壓縮列表,redis怎麼儲存score和value值呢?其實很簡單,每個集合的元素都使用兩個節點來儲存,第一個節點儲存的是成員(member),第二個元素儲存的是元素的分值(score)
1、編碼轉換
當有序集合物件可以同時滿足以下兩個條件時,使用ziplist編碼
- 有序集合的所有元素長度都小於64位元組
- 有序集合的元素數量小於128個;
2、配置
上述兩個條件是支援配置的。
- zset-max-ziplist-value
- zset-max-ziplist-entries
Hash型別
雜湊物件使用的是ziplist壓縮列表或hashtable雜湊表。(hashtable底層使用的是字典資料結構,我們並沒有在本文做詳細介紹,有需要可以自己瞭解)
1、編碼轉換
當雜湊物件同時滿足下面兩個條件,使用ziplist壓縮列表
- 雜湊物件儲存的所有鍵值對的鍵和值的字串長度都小於64位元組
- 雜湊物件儲存的鍵值對的數量小於512個;
2、配置
上述兩個條件是支援配置的。
- hash-max-ziplist-value
- hash-max-ziplist-entries