Redis 底層資料結構詳解

2021-10-24 05:35:16 字數 3699 閱讀 3126

前面介紹過redis的五大基礎資料型別(string、hash、list、set、zset)由六種底層資料結構(簡單動態字串、鍊錶、字典、跳躍表、整數集合、壓縮列表)實現,本章主要分析這六種底層資料結構。

ps:redis有八種編碼,但底層資料機構是六種。

結構定義

struct sdshdr
sds儲存的字串結構圖示:

優勢:

由於 len 屬性的存在,我們獲取 sds 字串的長度只需要讀取 len 屬性,時間複雜度為 o(1)。而對於 c 語言,獲取字串的長度通常是經過遍歷計數來實現的,時間複雜度為 o(n)。通過 strlen key 命令可以獲取 key 的字串長度。

在 c 語言中使用 strcat 函式來進行兩個字串的拼接,一旦沒有分配足夠長度的記憶體空間,就會造成緩衝區溢位。而對於 sds 資料型別,在進行字元修改的時候,會首先根據記錄的 len 屬性檢查記憶體空間是否滿足需求,如果不滿足,會進行相應的空間擴充套件,然後在進行修改操作,所以不會出現緩衝區溢位。

sds 除了儲存資料庫中的字串值以外,sds 還可以作為緩衝區(buffer):包括 aof 模組中的aof緩衝區以及客戶端狀態中的輸入緩衝區

鍊錶的定義

//鍊錶節點

typedef struct listnodelistnode

通過多個 listnode 結構就可以組成鍊錶,這是乙個雙端鍊錶,redis還提供了操作鍊錶的資料結構:

鍊錶特性字典又稱為符號表或者關聯陣列、或對映(map),是一種用於儲存鍵值對的抽象資料結構。字典中的每乙個鍵 key 都是唯一的,通過 key 可以對值來進行查詢或修改。

雜湊表結構定義:

typedef struct dicthtdictht
雜湊表是由陣列 table 組成,table 中每個元素都是指向 dict.h/dictentry 結構,dictentry 結構定義如下:

typedef struct dictentryv;

//指向下乙個雜湊表節點,形成鍊錶

struct dictentry *next;

}dictentry

key 用來儲存鍵,val 屬性用來儲存值,值可以是乙個指標,也可以是uint64_t整數,也可以是int64_t整數。

注意:這裡還有乙個指向下乙個雜湊表節點的指標,我們知道雜湊表最大的問題是存在雜湊衝突,如何解決雜湊衝突,有開放位址法和鏈位址法。這裡採用的便是鏈位址法,通過next這個指標可以將多個雜湊值相同的鍵值對連線在一起,用來解決雜湊衝突。

4.1 hash表特性

(1) 雜湊演算法:redis計算雜湊值和索引值方法如下:

#1、使用字典設定的雜湊函式,計算鍵 key 的雜湊值

hash = dict->type->hashfunction(key);

#2、使用雜湊表的sizemask屬性和第一步得到的雜湊值,計算索引值

index = hash & dict->ht[x].sizemask;

(3) 擴容和收縮:當雜湊表儲存的簡直對太多或太少的時候,就需要通過rehash(重新雜湊)來對雜湊表進行相應的擴充套件或者收縮。具體步驟:

(4) 觸發擴容的條件:

(5) 漸近式 rehash:

什麼叫漸進式 rehash?也就是說擴容和收縮操作不是一次性、集中式完成的,而是分多次、漸進式完成的。如果儲存在redis中的鍵值對只有幾個幾十個,那麼 rehash 操作可以瞬間完成,但是如果鍵值對有幾百萬,幾千萬甚至幾億,那麼要一次性的進行 rehash,勢必會造成redis一段時間內不能進行別的操作。所以redis採用漸進式 rehash,這樣在進行漸進式rehash期間,字典的刪除查詢更新等操作可能會在兩個雜湊表上進行,第乙個雜湊表沒有找到,就會去第二個雜湊表上進行查詢。但是進行 增加操作,一定是在新的雜湊表上進行的。

結構定義:

//列表節點

typedef struct ziplistnode

// 壓縮表

typedef struct ziplistziplist;

結構圖:

壓縮表特性:

節點資料

(1) previous_entry_ength:記錄壓縮列表前乙個位元組的長度。previous_entry_ength的長度可能是1個位元組或者是5個位元組,如果上乙個節點的長度小於254,則該節點只需要乙個位元組就可以表示前乙個節點的長度了,如果前乙個節點的長度大於等於254,則previous length的第乙個位元組為254,後面用四個位元組表示當前節點前乙個節點的長度。利用此原理即當前節點位置減去上乙個節點的長度即得到上乙個節點的起始位置,壓縮列表可以從尾部向頭部遍歷。這麼做很有效地減少了記憶體的浪費。

(2) encoding:節點的encoding儲存的是節點的content的內容型別以及長度,encoding型別一共有兩種,一種位元組陣列一種是整數,encoding區域長度為1位元組、2位元組或者5位元組長。

(3) content:content區域用於儲存節點的內容,節點內容型別和長度由encoding決定。

整數集合intset是redis用於儲存整數值的集合抽象資料型別,他可以儲存型別為int16_t,int32_t或者int64_t的整數值,並且保證集合中不會出現重複元素。

結構定義:

typedef struct intsetintset;
公升級

當我們新增的元素型別比原集合型別的長度要大時,需要對陣列集合進行公升級,才能將新元素放入整數集合中,具體步驟:

降級

整數集合不支援降級操作,一旦對陣列進行來公升級,編碼就會一直保持公升級後的狀態。

附:

Redis底層資料結構?

福哥口訣法 簡鏈字跳整 壓快壓 sds synamic string 簡單動態字串。支援自動動態擴容的位元組陣列 list 鍊錶 雙端鍊錶。dict 字典。使用雙雜湊表實現的,支援平滑擴容的字典 zskiplist 跳躍表。附加了後向指標的跳躍表 intset 整數集合。用於儲存整數數值集合的自有結...

Redis底層資料結構

redis底層實現的8種資料結構 sds synamic string 支援自動動態擴容的位元組陣列 list 鍊錶 dict 使用雙雜湊表實現的,支援平滑擴容的字典 zskiplist 附加了後向指標的跳躍表 intset 用於儲存整數數值集合的自有結構 ziplist 一種實現上類似於tlv,但...

redis底層資料結構

1.1 string字串 表現形式為 資料結構 sds 簡單的動態字串 使用原因 redis是使用c語言開發的,但在c語言中是沒有字串型別的,只能使用指標或符陣列的形式表示乙個字串,所以在redis設計了一種簡單的動態字串 可以根據不同的資料型別不同的資料結構選擇不同的資料結構 支援的資料型別 字串...