redis的五種資料結構原理分析

2021-08-31 18:38:03 字數 3991 閱讀 3772

redis是乙個開源的使用c語言編寫的乙個kv儲存系統,是乙個速度非常快的非關係遠端記憶體資料庫。它支援包括string、list、set、zset、hash五種資料結構。除此之外,通過複製、持久化和客戶端分片等特性,使用者可以很方便地將redis擴充套件成乙個能夠包含數百gb資料和每秒處理上百萬次的請求的系統。目前支援多種語言的api,方便使用者使用。

redis同時也內建了事務、lua指令碼、複製等功能,提供兩種持久化選項,一種是每隔一段時間將資料匯入到磁碟(快照模式),另一種是追加命令到日誌中(aof模式)。如果只是作為高效的記憶體資料庫使用也可以關閉持久化功能。通過哨兵(sentinel)和自動分割槽(cuuster)的方式可以提高redis伺服器的高可用性。

與關係型資料庫相比,redis的命令請求不需要經過查詢分析器或查詢優化器進行處理,也避免了更新資料時引起的隨機讀\寫,這些慢操作。它直接讀寫記憶體中的資料,並且資料是按照一定的資料結構儲存的。所以它的速度非常快。

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

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

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

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

由此可以得出以下特性:

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

當有任一條件 不滿足時將會進行一次轉碼,使用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節點儲存的是一片資料,而不是乙個資料。總結:

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

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

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

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

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

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的形式儲存。

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

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

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

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

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

跳躍表的實現原理可以參考:

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

快取會話(單點登入)

分布式鎖,比如:使用setnx

各種排行榜或計數器

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

使用list作為訊息對列

秒殺,庫存扣減等

hash,存放鍵值對,一般可以用來存某個物件的基本屬性資訊,例如,使用者資訊,商品資訊等,另外,由於hash的大小在小於配置的大小的時候使用的是ziplist結構,比較節約記憶體,所以針對大量的資料儲存可以考慮使用hash來分段儲存來達到壓縮資料量,節約記憶體的目的,例如,對於大批量的商品對應的位址名稱。比如:商品編碼固定是10位,可以選取前7位做為hash的key,後三位作為field,位址作為value。這樣每個hash表都不超過999個,只要把redis.conf中的hash-max-ziplist-entries改為1024,即可。

list,列表型別,可以用於實現訊息佇列,也可以使用它提供的range命令,做分頁查詢功能。

set,集合,整數的有序列表可以直接使用set。可以用作某些去重功能,例如使用者名稱不能重複等,另外,還可以對集合進行交集,並集操作,來查詢某些元素的共同點

zset,有序集合,可以使用範圍查詢,排行榜功能或者topn功能。

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

redis的五種資料結構原理分析

redis是乙個開源的使用c語言編寫的乙個kv儲存系統,是乙個速度非常快的非關係遠端記憶體資料庫。它支援包括string list set zset hash五種資料結構。除此之外,通過複製 持久化和客戶端分片等特性,使用者可以很方便地將redis擴充套件成乙個能夠包含數百gb資料和每秒處理上百萬次...

Redis五種資料結構

redis除了儲存鍵之外還可以儲存常見的5種資料型別,分別是 string list set zset hash。結構型別 結構儲存的值 結構的讀寫能力 string字串 可以是字串 整數或浮點數 對整個字串或字串的一部分進行操作 對整數或浮點數進行自增或自減操作 list列表 乙個鍊錶,鍊錶上的每...

Redis五種資料結構

對redis來說,所有的key 鍵 都是字串,所謂的5種資料結構是指針對value而言 資料結構型別 說明使用場景 常用方法 其他鏈結 string字串型別1 redis中最基本的資料型別,乙個key對應乙個value。2 是二進位制安全的,意思是 redis 的 string 可以包含任何資料。如...