redis中的list是乙個有序(按加入的時序排序)的資料結構,一般有序我們會採用陣列或者是雙向鍊錶,其中雙向鍊錶由於有前後指標實際上會很浪費記憶體。3.2版本之前採用兩種資料結構作為底層實現:
ziplist的資料結構及解釋如下:
[外鏈轉存失敗,源站可能有防盜煉機制,建議將儲存下來直接上傳(img-3sqbo573-1610531492142)(
ziplist
的節點資訊如下:
返回乙個zlenrtytypedef
struct zlentry zlentry;
ziplist是使用連續的記憶體塊儲存的:static zlentry zipentry
(unsigned
char
*p)
注意zllen用16個位元位儲存,也就是說起長度最大表示65535,所以如果長度超過這個值,只能夠通過節點遍歷來確定列表元素數量每個節點
zlentry
是如何儲存的,完整的zlentry
由以下幾個部分組成:
unsigned int lensize, len
: lensize表示編碼 len 所需的位元組大小,len表示當前節點值的長度
unsigned int headersize
: 當前節點 header 的大小,等於 prevrawlensize + lensize
unsigned char encoding
: 當前節點值所使用的編碼型別
ziplist資料結構中prevrawlen
是變長編碼的,如果上乙個節點長度小於254個位元組,則只是用乙個位元組儲存prevrawlen,如果大於等於254,那麼第乙個位元組用254標記,後面四個位元組表示上乙個節點長度
ziplist 最大的確定就是連鎖更新問題
因為在ziplist
中,每個zlentry
都儲存著前乙個節點所佔的位元組數,而這個數值又是變長編碼的。假設存在乙個壓縮列表,其包含 e1、e2、e3、e4…,e1 節點的大小為 253 位元組,那麼 e2.prevrawlen 的大小為 1 位元組,如果此時在 e2 與 e1 之間插入了乙個新節點 e,e 編碼後的整體長度(包含 e1 的長度)為 254 位元組,此時 e2.prevrawlen 就需要擴充為 5 位元組;如果 e2 的整體長度變化又引起了 e3.prevrawlen 的儲存長度變化,那麼 e3 也需要擴…如此遞迴直到表尾節點或者某乙個節點的 prevrawlen 本身長度可以容納前乙個節點的變化。其中每一次擴充都需要進行空間再分配操作。刪除節點亦是如此,只要引起了操作節點之後的節點的 prevrawlen 的變化,都可能引起連鎖更新,引發記憶體 realloc。
連鎖更新在最壞情況下需要進行 n 次空間再分配,而每次空間再分配的最壞時間複雜度為 o(n),因此連鎖更新的總體時間複雜度是 o(n^2)。
基於上述來看ziplist的缺點大於優點,所以3.2版本之後採用了另外乙個資料結構為quicklist
redis> rpush lst 1 3 5 10086 "hello"
"world"
(integer)6
redis> object encoding lst
"ziplist"
redis> hmset profile "name"
"jack"
"age" 28 "job"
"programmer"
ok
redis> object encoding profile
"ziplist"
雜湊鍵裡面包含的所有鍵和值都是小整數值或者短字串ps: 以上截圖來自《redis設計與實現一書》
Redis原始碼分析 intset h c
intset.h c 是redis 的整數set實現,intset的結構體如下 基本結構 typedef struct intset intset intset的第乙個成員encoding,表明contents中的儲存資料的資料長度,可以是16bits,32bits,64bits。第二個成員leng...
Redis原始碼分析系列
redis目前熱門nosql記憶體資料庫,量不是很大,本系列是本人閱讀redis原始碼時記錄的筆記,由於時間倉促和水平有限,文中難免會有錯誤之處,歡迎讀者指出,共同學習進步,本文使用的redis版本是2.8.19。redis之hash資料結構 redis之intset資料結構 redis之skipl...
redis原始碼分析 adlist
typedef struct listnode listnode 首先定義了乙個節點,包含前驅和後繼以及對應的value typedef struct listiter listiter list的迭代器,next指標和迭代方向 typedef struct list list 鍊錶內容 head和...