Redis 数据结构 :SDS、链表、字典、跳表、整数集合、压缩列表
文章目錄
- SDS
- 結(jié)構(gòu)分析
- 內(nèi)存策略
- 空間預(yù)分配
- 惰性空間釋放
- 總結(jié)
- 鏈表
- 結(jié)構(gòu)分析
- 總結(jié)
- 字典
- 結(jié)構(gòu)分析
- rehash
- 漸進(jìn)式rehash
- 總結(jié)
- 跳表
- 結(jié)構(gòu)分析
- 總結(jié)
- 整數(shù)集合
- 結(jié)構(gòu)分析
- 升級
- 降級
- 總結(jié)
- 壓縮列表
- 結(jié)構(gòu)分析
- 連鎖更新
- 總結(jié)
SDS
結(jié)構(gòu)分析
由于C字符串存在大量問題,所以在Redis中,并沒有使用C風(fēng)格字符串,而是自己構(gòu)建了一個(gè)簡單動(dòng)態(tài)字符串即SDS(simple dynamic string)
struct sdshdr { // buf 中已占用空間的長度int len;// buf 中剩余可用空間的長度int free;// 數(shù)據(jù)空間char buf[]; };為解決C字符串緩沖區(qū)溢出問題以及長度計(jì)算問題,SDS中引入了len來統(tǒng)計(jì)當(dāng)前已使用空間長度,free來計(jì)算剩余的空間長度
C字符串的主要缺陷就是因?yàn)樗鼪]有記錄自己的長度,而如果在需要了解長度時(shí),就只能通過O(N)的效率進(jìn)行一次遍歷
并且因?yàn)镃字符串沒有統(tǒng)計(jì)剩余空間的字段,也沒有容量字段,所以很容易就會(huì)因?yàn)閟trcat等函數(shù)造成緩沖區(qū)的溢出,為彌補(bǔ)這一缺陷,redis在sds中增加了free字段
通過標(biāo)記剩余空間,當(dāng)對SDS進(jìn)行插入操作時(shí),就會(huì)提前判斷當(dāng)前剩余空間是否足夠,如果不足則會(huì)先進(jìn)行空間的拓展,再進(jìn)行插入,這樣就解決了緩沖區(qū)溢出的問題
內(nèi)存策略
由于Redis作為一個(gè)高效的內(nèi)存數(shù)據(jù)庫,用于速度要求嚴(yán)苛,插入刪除頻繁
的場景,為了提高內(nèi)存分配的效率,防止大量使用內(nèi)存重分配而調(diào)用系統(tǒng)函數(shù)導(dǎo)致的性能損失問題(用戶態(tài)和內(nèi)核態(tài)的切換),Redis主要依靠空間預(yù)分配和惰性空間釋放來解決這個(gè)問題
空間預(yù)分配
為減少空間分配的次數(shù),當(dāng)需要進(jìn)行空間拓展時(shí),不僅僅會(huì)為SDS分配修改所必須要的空間,并且會(huì)為SDS預(yù)分配額外的未使用空間。
預(yù)分配未使用空間的策略如下
- 當(dāng)SDS修改后的長度小于1MB時(shí),將會(huì)預(yù)分配大小和當(dāng)前l(fā)en一樣的空間(free = len),也就是使空間增長一倍,來減少因?yàn)槌跏紩r(shí)申請大空間導(dǎo)致的連續(xù)分配問題
- 當(dāng)SDS修改后的長度大于等于1MB時(shí),每次分配都會(huì)分配1MB的空間,防止空間的浪費(fèi)。
惰性空間釋放
當(dāng)我們對SDS進(jìn)行刪除操作時(shí),并不會(huì)立即回收刪除后空余的空間,而是將空余空間以free字段記錄下來,以備后面使用。
這樣做的目的在于防止因?yàn)榭臻g縮短后因?yàn)樵俣炔迦雽?dǎo)致的空間拓展問題。
并且如果有需求需要真正釋放空間,Redis也提供了對應(yīng)的API,所以不必?fù)?dān)心會(huì)因?yàn)槎栊缘目臻g釋放而導(dǎo)致的內(nèi)存浪費(fèi)問題。
總結(jié)
比起 C 字符串, SDS 具有以下優(yōu)點(diǎn):
鏈表
結(jié)構(gòu)分析
typedef struct listNode {// 前置節(jié)點(diǎn)struct listNode *prev;// 后置節(jié)點(diǎn)struct listNode *next;// 節(jié)點(diǎn)的值void *value; } listNode;/** 雙端鏈表迭代器*/ typedef struct listIter {// 當(dāng)前迭代到的節(jié)點(diǎn)listNode *next;// 迭代的方向int direction; } listIter;/** 雙端鏈表結(jié)構(gòu)*/ typedef struct list {// 表頭節(jié)點(diǎn)listNode *head;// 表尾節(jié)點(diǎn)listNode *tail;// 節(jié)點(diǎn)值復(fù)制函數(shù)void *(*dup)(void *ptr);// 節(jié)點(diǎn)值釋放函數(shù)void (*free)(void *ptr);// 節(jié)點(diǎn)值對比函數(shù)int (*match)(void *ptr, void *key);// 鏈表所包含的節(jié)點(diǎn)數(shù)量unsigned long len;} list;
從上面的結(jié)構(gòu)可以看出,Redis的鏈表是一個(gè)帶頭尾的雙端無環(huán)鏈表,并且通過len字段記錄了鏈表節(jié)點(diǎn)的長度
同時(shí)為了實(shí)現(xiàn)多態(tài)與泛型,鏈表中還提供了dup,free,match屬性來設(shè)置相關(guān)的函數(shù),使得鏈表支持不同類型的值的存儲(chǔ)
總結(jié)
- 鏈表被廣泛用于實(shí)現(xiàn) Redis 的各種功能, 比如列表鍵, 發(fā)布與訂閱, 慢查詢, 監(jiān)視器, 等等。
- 每個(gè)鏈表節(jié)點(diǎn)由一個(gè) listNode 結(jié)構(gòu)來表示, 每個(gè)節(jié)點(diǎn)都有一個(gè)指向前置節(jié)點(diǎn)和后置節(jié)點(diǎn)的指針, 所以 Redis 的鏈表實(shí)現(xiàn)是雙端鏈表。
- 每個(gè)鏈表使用一個(gè) list 結(jié)構(gòu)來表示, 這個(gè)結(jié)構(gòu)帶有表頭節(jié)點(diǎn)指針、表尾節(jié)點(diǎn)指針、以及鏈表長度等信息。
- 因?yàn)殒湵肀眍^節(jié)點(diǎn)的前置節(jié)點(diǎn)和表尾節(jié)點(diǎn)的后置節(jié)點(diǎn)都指向 NULL , 所以 Redis 的鏈表實(shí)現(xiàn)是無環(huán)鏈表。
- 通過為鏈表設(shè)置不同的類型特定函數(shù), Redis 的鏈表可以用于保存各種不同類型的值。
字典
結(jié)構(gòu)分析
Redis的字典底層采用了哈希表來進(jìn)行實(shí)現(xiàn)。
首先看看字典底層哈希表的結(jié)構(gòu)
typedef struct dictht {// 哈希表數(shù)組dictEntry **table;// 哈希表大小unsigned long size;// 哈希表大小掩碼,用于計(jì)算索引值// 總是等于 size - 1unsigned long sizemask;// 該哈希表已有節(jié)點(diǎn)的數(shù)量unsigned long used;} dictht;
哈希表中記錄了當(dāng)前的總長度,已有節(jié)點(diǎn),以及當(dāng)前索引大小(用于哈希函數(shù)來計(jì)算節(jié)點(diǎn)位置)
為解決哈希沖突,Redis字典采用了鏈地址法來構(gòu)造了哈希桶的結(jié)構(gòu),也就是哈希數(shù)組中的每個(gè)元素都是一個(gè)鏈表。
下面來看看哈希節(jié)點(diǎn)的結(jié)構(gòu)
typedef struct dictEntry {// 鍵void *key;// 值union {void *val;uint64_t u64;int64_t s64;} v;// 指向下個(gè)哈希表節(jié)點(diǎn),形成鏈表struct dictEntry *next; } dictEntry;可以看到,為保證鍵值對適用于多重類型,key值使用的時(shí)void的形式,而value使用了64位有符號整型和64位無符號整型,void指針的一個(gè)聯(lián)合體,每個(gè)節(jié)點(diǎn)使用next來鏈接成一個(gè)鏈表
typedef struct dictType {// 計(jì)算哈希值的函數(shù)unsigned int (*hashFunction)(const void *key);// 復(fù)制鍵的函數(shù)void *(*keyDup)(void *privdata, const void *key);// 復(fù)制值的函數(shù)void *(*valDup)(void *privdata, const void *obj);// 對比鍵的函數(shù)int (*keyCompare)(void *privdata, const void *key1, const void *key2);// 銷毀鍵的函數(shù)void (*keyDestructor)(void *privdata, void *key);// 銷毀值的函數(shù)void (*valDestructor)(void *privdata, void *obj);} dictType;為保證字典具有多態(tài)及泛型,dictType中提供了如哈希函數(shù)以及K-V的各種操作函數(shù),使得字典適用于多重情景
rehash
/** 字典*/ typedef struct dict {// 類型特定函數(shù)dictType *type;// 私有數(shù)據(jù)void *privdata;// 哈希表dictht ht[2];// rehash 索引// 當(dāng) rehash 不在進(jìn)行時(shí),值為 -1int rehashidx; /* rehashing not in progress if rehashidx == -1 */// 目前正在運(yùn)行的安全迭代器的數(shù)量int iterators; /* number of iterators currently running */} dict;從字典的結(jié)構(gòu)中,我們可以看到里面同時(shí)存放了兩個(gè)哈希表,以及一個(gè)rehashidx屬性。
這就牽扯到了字典的核心之一,rehash。
Redis作為一個(gè)插入頻繁且對效率要求高的數(shù)據(jù)庫,當(dāng)插入的數(shù)據(jù)過多時(shí),就會(huì)因?yàn)楣1碇械呢?fù)載因子過高而導(dǎo)致查詢或者插入的效率降低,此時(shí)就需要通過rehash來進(jìn)行重新擴(kuò)容并重新映射。
但是如果只是用一個(gè)哈希表,映射時(shí)就會(huì)導(dǎo)致數(shù)據(jù)庫暫時(shí)不可用,作為一個(gè)使用頻繁的數(shù)據(jù)庫,短期的停機(jī)幾乎是不可容許的問題,所以Redis設(shè)計(jì)時(shí)采用了雙哈希的結(jié)構(gòu),并采用了漸進(jìn)式rehash的方法來解決這個(gè)問題。
rehash的步驟如下
- 為ht[1]的哈希表分配空間
- 將ht[0]中的鍵值對重新映射到ht[1]上
- 當(dāng)ht[0]的數(shù)據(jù)遷移完成,此時(shí)ht[0]為一個(gè)空表,此時(shí)釋放ht[0],并讓ht[1]成為新的ht[0],再為ht[1]創(chuàng)建一個(gè)新的空白哈希表,為下一次的rehash做準(zhǔn)備
漸進(jìn)式rehash
由于數(shù)據(jù)庫中可能存在大量的數(shù)據(jù),而rehash的時(shí)候又過長,為了避免因?yàn)閞ehash造成的服務(wù)器停機(jī),rehash的過程并不是一次完成的,而是一個(gè)多次的,漸進(jìn)式的過程。
在漸進(jìn)式rehash的時(shí)候,由于數(shù)據(jù)不斷的進(jìn)行遷移,無法確定數(shù)據(jù)處于哪一個(gè)表上, 此時(shí)如果進(jìn)行插入、刪除、查找的操作時(shí)就會(huì)在兩個(gè)表上進(jìn)行,如果在一個(gè)表中沒找到對應(yīng)數(shù)據(jù),就會(huì)到另一個(gè)表中繼續(xù)查找。
并且如果此時(shí)新插入節(jié)點(diǎn),都會(huì)統(tǒng)一的防止在新表ht[1]中,防止對ht[0]的rehash造成干擾,保證ht[0]節(jié)點(diǎn)的只減少不增加
總結(jié)
- 字典被廣泛用于實(shí)現(xiàn) Redis 的各種功能, 其中包括數(shù)據(jù)庫和哈希鍵。
- Redis 中的字典使用哈希表作為底層實(shí)現(xiàn), 每個(gè)字典帶有兩個(gè)哈希表, 一個(gè)用于平時(shí)使用, 另一個(gè)僅在進(jìn)行 rehash 時(shí)使用。
- 當(dāng)字典被用作數(shù)據(jù)庫的底層實(shí)現(xiàn), 或者哈希鍵的底層實(shí)現(xiàn)時(shí), Redis 使用 MurmurHash2 算法來計(jì)算鍵的哈希值。
- 哈希表使用鏈地址法來解決鍵沖突, 被分配到同一個(gè)索引上的多個(gè)鍵值對會(huì)連接成一個(gè)單向鏈表。
- 在對哈希表進(jìn)行擴(kuò)展或者收縮操作時(shí), 程序需要將現(xiàn)有哈希表包含的所有鍵值對 rehash 到新哈希表里面, 并且這個(gè) rehash 過程并不是一次性地完成的, 而是漸進(jìn)式地完成的。
跳表
跳表是一個(gè)較為少見的數(shù)據(jù)結(jié)構(gòu),如果不了解的可以看看我之前的博客
看了這篇博客,還敢說你不懂跳表嗎?
由于跳表的實(shí)現(xiàn)簡單且性能可與平衡樹相媲美,對于大量插入刪除的數(shù)據(jù)庫來說,跳表只需要進(jìn)行簡單的鏈表插入和索引的選拔,而不像平衡樹一樣需要進(jìn)行整體平衡的維持。并且由于在范圍查找上的效率遠(yuǎn)遠(yuǎn)強(qiáng)于平衡樹,所以Redis底層選取跳表來作為有序集合的底層之一。
結(jié)構(gòu)分析
typedef struct zskiplistNode {// 成員對象robj *obj;// 分值double score;// 后退指針struct zskiplistNode *backward;// 層struct zskiplistLevel {// 前進(jìn)指針struct zskiplistNode *forward;// 跨度unsigned int span;} level[];} zskiplistNode;跳躍表的查詢從最頂層出發(fā),通過前進(jìn)指針來往后查找,通過比較節(jié)點(diǎn)的分?jǐn)?shù)來判斷當(dāng)前節(jié)點(diǎn)是否與索引匹配,如果查找不到則進(jìn)入下層繼續(xù)查找,并記錄下跨越的層數(shù)span來進(jìn)行排位。
同時(shí)為了處理特殊情況,還準(zhǔn)備了一個(gè)后退指針來進(jìn)行從表尾到表頭的遍歷,但是與前進(jìn)不同,后退指針并不存在跳躍,而是只能一個(gè)一個(gè)向后查詢
跳躍表通過保存表頭和表尾節(jié)點(diǎn),來快速訪問表頭和表尾。并且保存了節(jié)點(diǎn)的數(shù)量來實(shí)現(xiàn)O(1)的長度計(jì)算
為了避免因?yàn)閷訑?shù)過高導(dǎo)致的大量空間損失,Redis跳躍表的節(jié)點(diǎn)高度最高位32層。
總結(jié)
- 跳躍表是有序集合的底層實(shí)現(xiàn)之一, 除此之外它在 Redis 中沒有其他應(yīng)用。
- Redis 的跳躍表實(shí)現(xiàn)由 zskiplist 和 zskiplistNode 兩個(gè)結(jié)構(gòu)組成, 其中 zskiplist 用于保存跳躍表信息(比如表頭節(jié)點(diǎn)、表尾節(jié)點(diǎn)、長度), 而 zskiplistNode 則用于表示跳躍表節(jié)點(diǎn)。
- 每個(gè)跳躍表節(jié)點(diǎn)的層高都是 1 至 32 之間的隨機(jī)數(shù)。
- 在同一個(gè)跳躍表中, 多個(gè)節(jié)點(diǎn)可以包含相同的分值, 但每個(gè)節(jié)點(diǎn)的成員對象必須是唯一的。
- 跳躍表中的節(jié)點(diǎn)按照分值大小進(jìn)行排序, 當(dāng)分值相同時(shí), 節(jié)點(diǎn)按照成員對象的大小進(jìn)行排序。
整數(shù)集合
整數(shù)集合時(shí)集合鍵的底層實(shí)現(xiàn)之一,當(dāng)集合中的元素全部都是整數(shù)值的時(shí)候,并且集合中元素不多時(shí),Redis就會(huì)使用整數(shù)集合來作為集合鍵的底層結(jié)構(gòu)。整數(shù)集合具有去重且排序的特性
結(jié)構(gòu)分析
typedef struct intset {// 編碼方式uint32_t encoding;// 集合包含的元素?cái)?shù)量uint32_t length;// 保存元素的數(shù)組int8_t contents[];} intset;在上面的結(jié)構(gòu)中,雖然content數(shù)組的類型是一個(gè)8bit的整型,但是數(shù)據(jù)真正存儲(chǔ)的方式并不是這個(gè)類型,而是根據(jù)encoding來決定具體的類型,8bit只是作為一個(gè)基本單位來進(jìn)行使用。
例如此時(shí)encoding設(shè)置為INTSET_ENC_INT16時(shí),數(shù)組存儲(chǔ)的格式就有每個(gè)元素16bit
如果encoding設(shè)置為INTSET_ENC_INT64時(shí),數(shù)組存儲(chǔ)的格式就有每個(gè)元素64bit
升級
升級的流程
新元素的插入位置
由于會(huì)引起升級的元素的類型都必頂比數(shù)組中的所有數(shù)據(jù)都大,所以也就決定了其要么比所有數(shù)據(jù)都大,要么比所有數(shù)據(jù)都小(負(fù)數(shù)),所以插入位置只能是首部和尾部
- 當(dāng)新元素比所有數(shù)據(jù)都大時(shí)在尾部插入
- 當(dāng)新元素比所有數(shù)據(jù)都小時(shí)在首部插入
如果插入一個(gè)32位的數(shù)據(jù),則引起全體升級
分配底層空間
整體升級并挪動(dòng)位置
插入元素
降級
在整數(shù)列表中升級是一個(gè)不可逆的過程,即使將所有高類型的數(shù)據(jù)刪除了,也不會(huì)進(jìn)行降級。
理由是防止因?yàn)榻导壓笤俅紊墡淼拇罅繑?shù)據(jù)挪動(dòng)的問題,在保證了效率的同時(shí),也帶來了一定程度上的空間浪費(fèi)(非必要時(shí)盡量不要升級)
升級帶來的好處
總結(jié)
- 整數(shù)集合是集合鍵的底層實(shí)現(xiàn)之一。
- 整數(shù)集合的底層實(shí)現(xiàn)為數(shù)組, 這個(gè)數(shù)組以有序、無重復(fù)的方式保存集合元素, 在有需要時(shí), 程序會(huì)根據(jù)新添加元素的類型, 改變這個(gè)數(shù)組的類型。
- 升級操作為整數(shù)集合帶來了操作上的靈活性, 并且盡可能地節(jié)約了內(nèi)存。
- 整數(shù)集合只支持升級操作, 不支持降級操作。
壓縮列表
壓縮列表(ziplist)是列表鍵和哈希鍵的底層實(shí)現(xiàn)之一。
當(dāng)一個(gè)列表鍵只包含少量列表項(xiàng), 并且每個(gè)列表項(xiàng)要么就是小整數(shù)值, 要么就是長度比較短的字符串, 那么 Redis 就會(huì)使用壓縮列表來做列表鍵的底層實(shí)現(xiàn)。主要核心就是為了節(jié)約空間
結(jié)構(gòu)分析
例如這張圖,可以看出當(dāng)前包含三個(gè)節(jié)點(diǎn),總空間為0x50(十進(jìn)制80),到尾部的偏移量為0x3c(十進(jìn)制60),節(jié)點(diǎn)數(shù)量為0x3(十進(jìn)制3)
每個(gè)壓縮列表節(jié)點(diǎn)可以由一個(gè)整數(shù)或者一個(gè)字節(jié)數(shù)組組成
整數(shù)的類型可以是以下六種之一:
- 4位的介于0-12的無符號整數(shù)
- 1字節(jié)的有符號整數(shù)
- 3字節(jié)的有符號整數(shù)
- int16_t
- int32_t
- int64_t
字節(jié)數(shù)組可以是以下三種之一:
- 長度小于等于63(2^6 -1)字節(jié)的字節(jié)數(shù)組
- 長度小于等于16383(2^14 -1)字節(jié)的字節(jié)數(shù)組
- 長度小于等于4294967295(2^32 -1)字節(jié)的字節(jié)數(shù)組
而壓縮列表節(jié)點(diǎn)又有三個(gè)屬性組成,分別是previous_entry_length,encoding,content。
previous_entry_length
這個(gè)屬性記錄了壓縮列表前一個(gè)節(jié)點(diǎn)的長度,該屬性根據(jù)前一個(gè)節(jié)點(diǎn)的大小不同可以是1個(gè)字節(jié)或者5個(gè)字節(jié)。
- 如果前一個(gè)節(jié)點(diǎn)的長度小于254個(gè)字節(jié),那么previous_entry_length的大小為1個(gè)字節(jié),即前一個(gè)節(jié)點(diǎn)的長度可以使用1個(gè)字節(jié)表示
- 如果前一個(gè)節(jié)點(diǎn)的長度大于等于254個(gè)字節(jié),那么previous_entry_length的大小為5個(gè)字節(jié),第一個(gè)字節(jié)會(huì)被設(shè)置為0xFE(十進(jìn)制的254),之后的四個(gè)字節(jié)則用于保存前一個(gè)節(jié)點(diǎn)的長度。
小于254字節(jié)時(shí)的表示
大于等于254字節(jié)時(shí)的表示
為什么要這樣設(shè)計(jì)呢?
由于壓縮列表中的數(shù)據(jù)以一種不規(guī)則的方式進(jìn)行緊鄰,無法通過后退指針來找到上一個(gè)元素,而通過保存上一個(gè)節(jié)點(diǎn)的長度,用當(dāng)前的地址減去這個(gè)長度,就可以很容易的獲取到了上一個(gè)節(jié)點(diǎn)的位置,通過一個(gè)一個(gè)節(jié)點(diǎn)向前回溯,來達(dá)到從表尾往表頭遍歷的操作
encoding
encoding通過以下規(guī)則來記錄content的類型
- 一字節(jié)、兩字節(jié)或者五字節(jié)長, 值的最高位為 00 、 01 或者 10 的是字節(jié)數(shù)組編碼: 這種編碼表示節(jié)點(diǎn)的 content 屬性保存著字節(jié)數(shù)組, 數(shù)組的長度由編碼除去最高兩位之后的其他位記錄;
- 一字節(jié)長, 值的最高位以 11 開頭的是整數(shù)編碼: 這種編碼表示節(jié)點(diǎn)的 content 屬性保存著整數(shù)值, 整數(shù)值的類型和長度由編碼除去最高兩位之后的其他位記錄;
content
content屬性負(fù)責(zé)保存節(jié)點(diǎn)的值,值的具體類型由上一個(gè)字段encoding來決定。
例如存儲(chǔ)字節(jié)數(shù)組,00表示類型為字節(jié)數(shù)組,01011表示長度為11
存儲(chǔ)整數(shù)值,表示存儲(chǔ)的為整數(shù),類型為int16_t
連鎖更新
當(dāng)添加或刪除節(jié)點(diǎn)時(shí),可能就會(huì)因?yàn)閜revious_entry_length的變化導(dǎo)致發(fā)生連鎖的更新操作。
假設(shè)e1的previous_entry_length只有1個(gè)字節(jié),而新插入的節(jié)點(diǎn)大小超過了254字節(jié),此時(shí)由于e1
的previous_entry_length無法該長度,就會(huì)將previous_entry_length的長度更新為5字節(jié)。
但是問題來了,假設(shè)e1原本的大小為252字節(jié),當(dāng)previous_entry_length更新后它的大小則超過了254,此時(shí)又會(huì)引發(fā)對e2的更新。
順著這個(gè)思路,一直更新下去
同理,刪除也會(huì)引發(fā)連鎖的更新
從上圖可以看出來,在最壞情況下,會(huì)從插入位置一直連鎖更新到末尾,即執(zhí)行了N次空間重分配, 而每次空間重分配的最壞復(fù)雜度為 O(N) , 所以連鎖更新的最壞復(fù)雜度為 O(N^2) 。
即使存在這種情況,但是并不影響我們使用壓縮列表
- 壓縮列表里要恰好有多個(gè)連續(xù)的、長度介于 250 字節(jié)至 253 字節(jié)之間的節(jié)點(diǎn), 連鎖更新才有可能被引發(fā), 這種情況就和連中彩票一樣,很少見
- 即使出現(xiàn)連鎖更新, 但只要被更新的節(jié)點(diǎn)數(shù)量不多, 就不會(huì)對性能造成任何影響: 比如說, 對三五個(gè)節(jié)點(diǎn)進(jìn)行連鎖更新是絕對不會(huì)影響性能的;
總結(jié)
- 壓縮列表是一種為節(jié)約內(nèi)存而開發(fā)的順序型數(shù)據(jù)結(jié)構(gòu)。
- 壓縮列表被用作列表鍵和哈希鍵的底層實(shí)現(xiàn)之一。
- 壓縮列表可以包含多個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)可以保存一個(gè)字節(jié)數(shù)組或者整數(shù)值。
- 添加新節(jié)點(diǎn)到壓縮列表, 或者從壓縮列表中刪除節(jié)點(diǎn), 可能會(huì)引發(fā)連鎖更新操作, 但這種操作出現(xiàn)的幾率并不高。
總結(jié)
以上是生活随笔為你收集整理的Redis 数据结构 :SDS、链表、字典、跳表、整数集合、压缩列表的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高级数据结构与算法 | 跳跃表(Skip
- 下一篇: linux cmake编译源码,linu