redis資料結構

2021-09-11 04:22:04 字數 3896 閱讀 8541

五種資料結構

字串(string)

與其它程式語言或其它鍵值儲存提供的字串非常相似,鍵(key)------值(value) (字串格式),字串擁有一些操作命令,如:get set del 還有一些比如自增或自減操作等等。redis是使用c語言開發,但c中並沒有字串型別,只能使用指標或符陣列的形式表示乙個字串,所以redis設計了一種簡單動態字串(sds[****** dynamic string])作為底實現:

定義sds物件,此物件中包含三個屬性:

len buf中已經占有的長度(表示此字串的實際長度)

free buf中未使用的緩衝區長度

buf 實際儲存字串資料的地方

所以取字串的長度的時間複雜度為o(1),另,buf中依然採用了c語言的以\0結尾可以直接使用c語言的部分標準c字串庫函式。

空間分配原則:當len小於imb(1024*1024)時增加字串分配空間大小為原來的2倍,當len大於等於1m時每次分配 額外多分配1m的空間。

由此可以得出以下特性:

redis為字元分配空間的次數是小於等於字串的長度n,而原c語言中的分配原則必為n。降低了分配次數提高了追加速度,代價就是多占用一些記憶體空間,且這些空間不會自動釋放。

二進位制安全的

高效的計算字串長度(時間複雜度為o(1))

高效的追加字串操作。

列表(list)

redis對鍵表的結構支援使得它在鍵值儲存的世界中獨樹一幟,乙個列表結構可以有序地儲存多個字串,擁有例如:lpush lpop rpush rpop等等操作命令。在3.2版本之前,列表是使用ziplist和linkedlist實現的,在這些老版本中,當列表物件同時滿足以下兩個條件時,列表物件使用ziplist編碼:

列表物件儲存的所有字串元素的長度都小於64位元組

列表物件儲存的元素數量小於512個

當有任一條件 不滿足時將會進行一次轉碼,使用linkedlist。

而在3.2版本之後,重新引入了乙個quicklist的資料結構,列表的底層都是由quicklist實現的,它結合了ziplist和linkedlist的優點。按照原文的解釋這種資料結構是【a doubly linked list of ziplists】意思就是乙個由ziplist組成的雙向鍊錶。那麼這兩種資料結構怎麼樣結合的呢?

ziplist的結構

由表頭和n個entry節點和壓縮列表尾部識別符號zlend組成的乙個連續的記憶體塊。然後通過一系列的編碼規則,提高記憶體的利用率,主要用於儲存整數和比較短的字串。可以看出在插入和刪除元素的時候,都需要對記憶體進行一次擴充套件或縮減,還要進行部分資料的移動操作,這樣會造成更新效率低下的情況。

linkedlist的結構

意思為乙個雙向鍊錶,和普通的鍊錶定義相同,每個entry包含向前向後的指標,當插入或刪除元素的時候,只需要對此元素前後指標操作即可。所以插入和刪除效率很高。但查詢的效率卻是o(n)[n為元素的個數]。

了解了上面的這兩種資料結構,我們再來看看上面說的「ziplist組成的雙向鍊錶」是什麼意思?實際上,它整體巨集觀上就是乙個鍊錶結構,只不過每個節點都是以壓縮列表ziplist的結構儲存著資料,而每個ziplist又可以包含多個entry。也可以說乙個quicklist節點儲存的是一片資料,而不是乙個資料。總結:

整體上quicklist就是乙個雙向鍊錶結構,和普通的鍊錶操作一樣,插入刪除效率很高,但查詢的效率卻是o(n)。不過,這樣的鍊錶訪問兩端的元素的時間複雜度卻是o(1)。所以,對list的操作多數都是poll和push。

每個quicklist節點就是乙個ziplist,具備壓縮列表的特性。

在redis.conf配置檔案中,有兩個引數可以優化列表:

list-max-ziplist-size 表示每個quicklistnode的位元組大小。預設為-2 表示8kb

list-compress-depth 表示quicklistnode節點是否要壓縮。預設是0 表示不壓縮

雜湊(hash)

redis的雜湊可以儲存多個鍵 值 對之間的對映,雜湊儲存的值既可以是字串又可以是數字值,並且使用者同樣可以對雜湊儲存的數字值執行自增操作或者自減操作。雜湊可以看作是乙個文件或關聯式資料庫裡的一行。hash底層的資料結構實現有兩種:

一種是ziplist,上面已經提到過。當儲存的資料超過配置的閥值時就是轉用hashtable的結構。這種轉換比較消耗效能,所以應該盡量避免這種轉換操作。同時滿足以下兩個條件時才會使用這種結構:

當鍵的個數小於hash-max-ziplist-entries(預設512)

當所有值都小於hash-max-ziplist-value(預設64)

另一種就是hashtable。這種結構的時間複雜度為o(1),但是會消耗比較多的記憶體空間。

集合(set)

redis的集合和列表都可以儲存多個字串,它們之間的不同在於,列表可以儲存多個相同的字串,而集合則通過使用雜湊表(hashtable)來保證自已儲存的每個字串都是各不相同的(這些雜湊表只有鍵,但沒有與鍵相關聯的值),redis中的集合是無序的。還可能存在另一種集合,那就是intset,它是用於儲存整數的有序集合,裡面存放同一型別的整數。共有三種整數:int16_t、int32_t、int64_t。查詢的時間複雜度為o(logn),但是插入的時候,有可能會涉及到公升級(比如:原來是int16_t的集合,當插入int32_t的整數的時候就會為每個元素公升級為int32_t)這時候會對記憶體重新分配,所以此時的時間複雜度就是o(n)級別的了。注意:intset只支援公升級不支援降級操作。

intset在redis.conf中也有乙個配置引數set-max-intset-entries預設值為512。表示如果entry的個數小於此值,則可以編碼成redis_encoding_intset型別儲存,節約記憶體。否則採用dict的形式儲存。

有序集合(zset)

有序集合和雜湊一樣,都用於儲存鍵值對:有序集合的鍵被稱為成員(member),每個成員都是各不相同的。有序集合的值則被稱為分值(score),分值必須為浮點數。有序集合是redis裡面唯一乙個既可以根據成員訪問元素(這一點和雜湊一樣),又可以根據分值以及分值的排列順序訪問元素的結構。它的儲存方式也有兩種:

是ziplist結構。

與上面的hash中的ziplist類似,member和score順序存放並按score的順序排列

另一種是skiplist與dict的結合。

skiplist是一種跳躍表結構,用於有序集合中快速查詢,大多數情況下它的效率與平衡樹差不多,但比平衡樹實現簡單。redis的作者對普通的跳躍表進行了修改,包括新增span\tail\backward指標、score的值可重複這些設計,從而實現排序功能和反向遍歷的功能。

一般跳躍表的實現,主要包含以下幾個部分:

表頭(head):指向頭節點

表尾(tail):指向尾節點

節點(node):實際儲存的元素節點,每個節點可以有多層,層數是在建立此節點的時候隨機生成的乙個數值,而且每一層都是乙個指向後面某個節點的指標。

層(level):目前表內節點的最大層數

長度(length):節點的數量。

跳躍表的遍歷總是從高層開始,然後隨著元素值範圍的縮小,慢慢降低到低層。

前面也說了,有序列表是使用skiplist和dict結合實現的,skiplist用來保障有序性和訪問查詢效能,dict就用來儲存元素資訊,並且dict的訪問時間複雜度為o(1)。

應用場景

redis一般應用場景

快取會話(單點登入)

分布式鎖,比如:使用setnx

各種排行榜或計數器

商品列表或使用者基礎資料列表等

使用list作為訊息對列

秒殺,庫存扣減等

總結本章介紹了redis的五種資料結構和它們使用的底層儲存原理,為了達到節省記憶體和快速訪問的目的每種資料結構可能有兩種儲存和訪問結構,在必要的時候會由一種結構轉換成另一種結構,但這個轉換的過程會消耗系統效能和記憶體空間的,所以在使用的過程中需要注意這些配置引數,開發中盡量避免達到這些峰值,使得redis能夠持續的提供高效的服務。

原文: 

Redis資料結構

字典 dict 是redis裡最核心的資料結構,正如其全稱remote dictionary service所說,redis其實就是乙個字典服務,字典以key value的形式呈現給使用者,key是簡單的字串,而value可以是各種資料結構,比如字串 string 鍊錶 list 集合 set 排序...

Redis 資料結構

最近接觸到了redis的使用,借這個機會深入的了解一下redis的實現和設計原理。下面先介紹一下redis底層所用到的資料結構。redis的實現幾乎都是基於下面的幾個資料結構之上的。struct sdshdr struct listnode struct list struct dictentry ...

redis 資料結構

今天學習了redis的列表型別 lpush ltrim lrange lpush mylist content ltrim 0,99 lrange 0,1 lrange 兩個引數 分別代表第乙個元素和最後乙個元素 redis的列表型別,可以用來做訊息佇列 使用乙個程序 用lpush命名作為生產者 使...