Redis基本資料型別底層資料結構

coffeebabe發表於2021-12-01

Redis資料型別底層資料結構

Redis目前基本的資料型別有String、List、Set、ZSet、Hash五種,首先Redis是C語言開發的,所以底層就是用C語言封裝資料結構或者C語言本身提供的資料結構來儲存。redis內部的主要資料結構主要有簡單字串(SDS)、雙端連結串列、字典、壓縮列表、跳躍表、整數集合。Redis內部並沒有直接使用這些資料結構來實現鍵值對資料庫,而是基於這些資料結構建立了一個物件系統,這個物件系統包含了我們所熟知的五種基本型別資料,也就是字串物件、列表物件、雜湊物件、集合物件和有序集合物件這五種型別的物件。而它們每一種物件都使用到了至少一種前面所介紹的資料結構。下面介紹一下redis內部的主要幾個資料結構簡單字串(SDS)、雙端連結串列、壓縮列表、跳躍表的定義。然後再介紹一下redis基本的五種資料型別,也就是五種型別的物件用到了上面的哪些資料結構。


redis的資料結構

SDS(Simple Dynamic String)簡單字串

1、redis定義:
SDS型別定義

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物件定義

redisObject物件定義

type屬性

  • 資料庫的key值都是一個string字串物件

2、編碼常量:
image

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


相關文章