基本數(shù)據(jù)結(jié)構(gòu)
簡(jiǎn)單動(dòng)態(tài)字符串
redis中的字符串使用“簡(jiǎn)單動(dòng)態(tài)字符串”(SDS)表示,無(wú)論是字符串值還是鍵底層都采用“簡(jiǎn)單動(dòng)態(tài)字符串”。
- free:未使用空間大小;
- len:字符串長(zhǎng)度;
- buf:以空字符結(jié)尾的char數(shù)組。
為了減少內(nèi)存重新分配次數(shù),SDS做出了以下優(yōu)化:
- 空間預(yù)分配:額外分配的未使用空間數(shù)量由以下公式?jīng)Q定: 如果對(duì)SDS進(jìn)行修改之后,SDS的長(zhǎng)小于1MB,那么程序分配和len 屬性同樣大小的未使用空間, 如果對(duì)SDS進(jìn)行修改之后,SDS的長(zhǎng)度將大于等千1MB, 那么程序會(huì)分配 1MB 的未使用空間。
- 惰性空間釋放:程序并不立即使用內(nèi)存重分配來回收縮短后多出來的字節(jié),而是使用free屬性將這些字節(jié)的數(shù)量記錄起來,并等待將來使用。
鏈表
鏈表是Redis列表鍵實(shí)現(xiàn)之一,也是很多其他功能實(shí)現(xiàn)的基礎(chǔ),鏈表節(jié)點(diǎn)定義如下:
鏈表的完整結(jié)構(gòu)體定義如下
- head為表頭指針;
- tail為表尾指針;
- len為鏈表長(zhǎng)度計(jì)數(shù)器;
- dup為函數(shù)指針,用于復(fù)制鏈表節(jié)點(diǎn)所保存的值;
- free為函數(shù)指針,用于釋放鏈表節(jié)點(diǎn)所保存的值;
- match為函數(shù)指針,則用于對(duì)比鏈表節(jié)點(diǎn)所保存的值和另一個(gè)輸入值是否相等。
字典
字典將鍵和值進(jìn)行關(guān)聯(lián),當(dāng)哈希鍵中的鍵值對(duì)數(shù)量比較多,或者鍵值對(duì)中的元素比較大的時(shí)候,采用字典作為底層實(shí)現(xiàn)。字節(jié)的數(shù)據(jù)結(jié)構(gòu)如下
哈希表結(jié)構(gòu)dict中,table屬性是一個(gè)數(shù)組,每個(gè)元素都是指向dictEntry結(jié)構(gòu)的指針,size屬性記錄了哈希表的大小,sizemask屬性的值總是等于size-1,而used屬性則記錄了哈希表目前已有節(jié)點(diǎn)(鍵值對(duì))的數(shù)量。
字典結(jié)構(gòu)dictType中有兩個(gè)哈希表ht[0]和ht[1],ht[l]哈希表只會(huì)在對(duì) ht[0]哈希表進(jìn)行rehash時(shí)使用,rehashidx它記錄了rehash目前的進(jìn)度。type屬性是一個(gè)指向dictType結(jié)構(gòu)的指針,dictType結(jié)構(gòu)保存了一簇用于操作特定類型鍵值對(duì)的函數(shù),例如計(jì)算哈希值、復(fù)制鍵、復(fù)制值、對(duì)比鍵、銷毀鍵和銷毀值的函數(shù)。而privdata屬性則保存了需要傳給那些類型特定函數(shù)的可選參數(shù)。
為了讓哈希表的負(fù)載因子維持在一個(gè)合理的范圍之內(nèi),當(dāng)哈希表保存的鍵值對(duì)數(shù)量太多或者太少時(shí),程序需要對(duì)哈希表的大小進(jìn)行相應(yīng)的擴(kuò)展或者收縮。
- 如果執(zhí)行的是擴(kuò)展操作,那么ht[l]的大小為第一個(gè)大于等于ht[0].used*2的;
- 如果執(zhí)行的是收縮操作,那么ht[1]的大小為第一個(gè)大于等于ht[O].used的。
字典采用漸進(jìn)式rehash,好處在千它采取分而治之的方式,將 rehash鍵值對(duì)所需的計(jì)算工作均攤到對(duì)字典的每個(gè)添加、刪除、查找和更新操作上。
跳躍表
跳躍表可以用于有序集合鍵的底層實(shí)現(xiàn),數(shù)據(jù)結(jié)構(gòu)如下
zskiplist結(jié)構(gòu)包含以下屬性:
- header: 指向跳躍表的表頭節(jié)點(diǎn)。
- tail: 指向跳躍表的表尾節(jié)點(diǎn)。
- level: 記錄目前跳躍表內(nèi),層數(shù)最大的那個(gè)節(jié)點(diǎn)的層數(shù)。
- length: 記錄跳躍表的長(zhǎng)度。
zskiplistNode 結(jié)構(gòu),該結(jié)構(gòu)包含以下屬性:
- 層 (level) : 每個(gè)層都帶有兩個(gè)屬性:前進(jìn)指針和跨度。前進(jìn)指針用于 訪問位于表尾方向的其他節(jié)點(diǎn),而跨度則記錄了前進(jìn)指針?biāo)赶蚬?jié)點(diǎn)和當(dāng)前節(jié)點(diǎn)的 距離。
- 后退 (backward) 指針:指向位于當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)。
- 分值 (score): 節(jié)點(diǎn)按各自所保存的分值從小到大排列。
- 成員對(duì)象 (obj): 節(jié)點(diǎn)所保存的成員對(duì)象。
整數(shù)集合
當(dāng)一個(gè)集合只包含整數(shù)值元素,并且這個(gè)集合的元素?cái)?shù)董不多時(shí), Redis 就會(huì)使用整數(shù)集合作為集合鍵的底層實(shí)現(xiàn)。
contents數(shù)組是整數(shù)集合的底層數(shù)據(jù)存放位置,各個(gè)項(xiàng)在數(shù)組中按值的大小從小到大有序地排列,并且數(shù)組中不包含任何重復(fù)項(xiàng)。length屬性記錄了整數(shù)集合包含的元素?cái)?shù)量,encoding屬性決定了整數(shù)類型(INTSET_ENC_INT16/INTSET_ENC_INT32/INTSET_ENC_INT64)。新元素的類型比整數(shù)集合現(xiàn)有所有元素的類型都要長(zhǎng)時(shí),整數(shù)集合需要先進(jìn)行升級(jí)。
壓縮鏈表
如果列表鍵或者哈希鍵包含的元素比較少,那么會(huì)采用壓縮列表作為底層實(shí)現(xiàn)。
entryX的數(shù)據(jù)結(jié)構(gòu)如下
節(jié)點(diǎn)的previous_entry_length記錄了壓縮列表中前一個(gè)節(jié) 點(diǎn)的長(zhǎng)度,節(jié)點(diǎn)的encoding屬性記錄了節(jié)點(diǎn)的content屬性所保存數(shù)據(jù)的類型以及長(zhǎng)度,節(jié)點(diǎn)的content屬性負(fù)責(zé)保存節(jié)點(diǎn)的值。
數(shù)據(jù)結(jié)構(gòu)和對(duì)象
Redis對(duì)象的結(jié)構(gòu)體定義如下
而對(duì)象具體使用的數(shù)據(jù)結(jié)構(gòu)可以用OBJECT ENCODING命令獲取。
不同類型的對(duì)象的編碼選擇規(guī)則如下:
字符串對(duì)象
- 如果一個(gè)字符串對(duì)象保存的是整數(shù)值,并且這個(gè)整數(shù)值可以用 long 類型來表示,那么 字符串對(duì)象會(huì)將整數(shù)值保存在字符串對(duì)象結(jié)構(gòu)的 ptr 屬性里面
- 如果字符串對(duì)象保存的是一個(gè)字符串值,并且這個(gè)字符串值的長(zhǎng)度大于 32 字節(jié),那么 字符串對(duì)象將使用一個(gè)簡(jiǎn)單動(dòng)態(tài)字符串 (SDS) 來保存這個(gè)字符串值
- 如果字符串對(duì)象保存的是一個(gè)字符串值,并且這個(gè)字符串值的長(zhǎng)度小千等于 32 字節(jié), 那么字符串對(duì)象將使用 embstr 編碼的方式來保存這個(gè)字符串值。
列表對(duì)象
當(dāng)列表對(duì)象可以同時(shí)滿足以下兩個(gè)條件時(shí),列表對(duì)象使用ziplist編碼:
- 列表對(duì)象保存的所有字符串元素的長(zhǎng)度都小千 64 字節(jié);
- 列表對(duì)象保存的元素?cái)?shù)量小千 512 個(gè);
不能滿足這兩個(gè)條件的列表對(duì)象需要使用 linkedlist 編碼。
恰希對(duì)象
當(dāng)哈希對(duì)象可以同時(shí)滿足以下兩個(gè)條件時(shí),哈希對(duì)象使用 ziplist 編碼:
- 哈希對(duì)象保存的所有鍵值對(duì)的鍵和值的字符串長(zhǎng)度都小千 64 字節(jié);
- 哈希對(duì)象保存的鍵值對(duì)數(shù)量小千 512 個(gè);
不能滿足這兩個(gè)條件的哈希對(duì)象需要使用 hash able 編碼。
集合對(duì)象
當(dāng)集合對(duì)象可以同時(shí)滿足以下兩個(gè)條件時(shí),對(duì)象使用 intset 編碼: 集合對(duì)象保存的所有元素都是整數(shù)值;
- 集合對(duì)象保存的元素?cái)?shù)量不超過 512 個(gè)。
不能滿足這兩個(gè)條件的集合對(duì)象需要使用 hash table 編碼。
有序集合對(duì)象
當(dāng)有序集合對(duì)象可以同時(shí)滿足以下兩個(gè)條件時(shí),對(duì)象使用ziplist編碼:
- 有序集合保存的元素?cái)?shù)量小于 128 個(gè);
- 有序集合保存的所有元素成員的長(zhǎng)度都小于 64 字節(jié);
不能滿足以上兩個(gè)條件的有序集合對(duì)象將使用skiplist編碼。
有序集合對(duì)象在維護(hù)skiplist的同時(shí),使用了dict,使得能夠快速完成成員查詢。