剖析Redis常用資料型別對應的資料結構

2021-09-24 06:29:20 字數 3073 閱讀 6762

------ 本文是學習演算法的筆記,《資料結構與演算法之美》,極客時間的課程 ------

今天來看下,經典資料庫 redis 中常用資料型別,底層都是用哪種資料結構實現的?

redis 是一種鍵值(key-value)資料庫。相對於關係型資料庫,redis 也被叫作非關係型資料庫

你mysql這樣的關係型資料庫,表的結構比較複雜,會包含很多字段,可以通過sql語句,來實現非常複雜的查詢需求。而redis客戶只包含「鍵」和「值」兩部分,只能通過「鍵」來查詢「值」。正是因為這樣簡單的儲存結構,也讓redis的讀寫效率非常高。

除此之外,redis主要是作為記憶體資料庫來使用,也就是說,資料是儲存在記憶體中的。儘管它經常被用作記憶體資料庫,但是,它也支援將資料儲存在硬碟中。

redis中,鍵的資料型別是字串,但是為了豐富資料儲存的方式,方便開發者使用,值的資料型別很多,它們分別是字串、列表、字典、集合、有序集合。

「字串(string)」這種資料型別非常簡單,對應到資料結構裡,就是字串。你應該非常熟悉了,這裡就不多介紹了。

列表(list)

列表這種資料型別支援儲存一組資料。這咱資料型別對應兩種實現方法,一種是壓縮列表(ziplist),另一種是雙向迴圈鍊錶。

列表中儲存的資料量比較小的時候,列表就可以採用壓縮列表的方式實現。具體需要同時滿足以下兩個條件:

關於壓縮列表,不是基礎資料結構,而是redis自己設計的一種資料儲存結構。它有點類似陣列,通過一片連續的記憶體空間,來儲存資料。不過,它跟陣列不同的一點是,它允許儲存的資料大小不同。具體的儲存結構也非常簡單,如下圖

來看下,壓縮列表中的「壓縮」該如何理解?

之所以說這種儲存結構節省記憶體,是相較於陣列的儲存思路而言的。我們知道,陣列要求每個元素大小相同,如果我們要儲存不同長度的字串,那我們就需要最大長度的字串大小作為元素的大小(假設是20個位元組)。那當我們儲存小於20個位元組長度的字串的時候,便會浪費部分儲存空間。

壓縮列表這種儲存結構,一方面比較節省記憶體,另一方面可以支援不同型別的資料。而且,因為資料儲存在一片連續的記憶體空間,通過鍵來獲取值為列表型別的資料,讀取的效率也非常高。

當列表中儲存的資料量比較大的時候,也就是不能同時滿足剛剛講的兩個條件的時候,列表就要通過雙向迴圈鍊錶來實現了。

redis的這種雙向鍊錶實現方式,非常值得借鑑。它額外定義乙個list結構體,並組織鍊錶的首、尾指標,還有長度資訊。這樣,在使用的時候就會非常方便。

字典(hash)

字典型別用來儲存一組資料時。每個資料又包含鍵值兩部分。字典型別也有兩種實現方式。一種是剛剛說的壓縮列表,另一種是雜湊表。

同樣,只有當儲存資料量比較小的情況下,redis 才使用壓縮列表來實現字典型別。具體需要滿足兩個兩件:

當不能同時滿足上面兩個條件時,redis 就使用雜湊表來實現字典型別。redis 使用 murmurhash2這種執行速度快、隨機性好的雜湊演算法作為雜湊函式。對於雜湊衝突問題,redis 使用鍊錶法來解決。除此之外,redis 還支援雜湊表的動態擴容、縮容。

當資料動態增加之後,雜湊表的裝載因子會不停的變大。為了避免雜湊表效能下降,當裝載因子大於1的時候,redis 會觸發擴容,將雜湊表擴大到原來大小的2倍左右。

擴容縮容要做大量資料搬移和雜湊值的重新計算,所以比較耗時。針對這個問題,redis使用漸進式擴容策略,將資料的搬移分批進行,避免大量資料一次性搬移的服務停頓。

集合(set)

集合這種資料結構型別用來儲存一組不重複的資料。其實現方法有兩種,一種是基於有序陣列,另一種是基於雜湊表。

當要儲存的資料,同時滿足正面兩個條件時候,redis 就採用有序陣列,來實現集合這種資料型別。

當不能同時滿足這兩個條件的時候,redis 就使用雜湊表來儲存集合中的資料。

有序集合(sortedset)

有序集合這種資料結構,我們在跳表那節就說過。它用來儲存一組資料,並且每個資料會附帶乙個得分。通過得分的大小,我們將資料組織成跳表這樣的資料結構,以支援快速地按照得分值、得分區間獲取資料。

實際上,跟redis 的其它資料型別一樣,有序集合也並不僅僅只有跳表這一種實現方式。當資料量較小的時候,redis 會使用壓縮列表來實現有序集合。具體點說,使用壓縮列表實現的有序集合的前提有兩個:

資料結構持久化

儘管redis 經常會被用途記憶體資料庫,但是,它也支援資料落盤,也就是將記憶體中的資料儲存到硬碟中。這樣,當機器斷電的時候,儲存在redis中的資料也不會丟失。在機器重新啟動之後,redis 只需要將在座在硬碟中的資料,重新讀取到記憶體,就可能正常工作了。

剛剛我們講到,redis的資料格式由「鍵」和「值」兩部分組成。而「值」又支援很多資料型別,比如字串、列表、字典、集合、有序集合、像字典、集合等型別,底層用到了雜湊表,雜湊表中有指標的概念,而指標指向的是記憶體中的儲存位址。那redis是如何將這樣乙個跟具體記憶體位址有關的資料結構儲存到磁碟中的呢?

實際上,redis遇到的這個問題並不特殊,很多場景中都會遇到。 我們把它叫作資料結構的持久化問題,或者物件的持久化問題

如何將資料結構持久化到硬碟?我們主要有兩種解決思路。

第一種清除原有的儲存結構,只將資料儲存到磁碟中。當我們需要從磁碟還原資料到記憶體的時候,再重新將資料組織成原來的資料結構。實際上,redis 採用的就是這種持久化思路國。

不過,這種方式也有一定的弊端。那就是資料從硬碟還原到記憶體的過程,會耗用比較多的時間。比如我們現在要將散表中的資料儲存到磁碟。當我們從磁碟中,取出資料重新構建雜湊表的時候,需要重新計算每個資料的雜湊值。如果磁碟中儲存的是幾gb的資料,那重建資料結構的耗時就不可忽視了。

第二種方式是保留原來的資料結構,將資料按原有的柳工在你在磁碟中。我們拿到雜湊表這樣的資料結構來舉例。我們可以將雜湊表的大小,每個資料被雜湊到的槽的編號等資訊,都儲存在磁碟中。這樣在還原資料到記憶體時,就可以避免重新計算雜湊值。

redis常用資料型別對應的資料結構

redis的資料型別都是通過多種資料結構來實現,主要是出於時間和空間的考慮,當資料量小的時候通過陣列下標訪問最快,占用記憶體最小 壓縮列表是陣列的變種,允許儲存的資料大小不同 因為陣列需要占用連續的記憶體空間,所以當資料量大的時候,就需要使用鍊錶,同時為了保證速度又需要和陣列結合,也就有了雜湊表。1...

Redis常用資料型別

redis最為常用的資料型別主要有以下五種 string hash list set sorted set 下面我們先來逐一的分析下這五種資料型別的使用和內部實現方式 string 常用命令 set,get,decr,incr,mget 等。應用場景 string是最常用的一種資料型別,普通的key...

redis常用資料型別

redis 特點 1.所有資料儲存在記憶體中,高速讀寫 2.提供豐富多樣的資料型別 string hash list set sorted set 3.提供了 aof 和 rdb 兩種資料的持久化儲存方式,保證了 redis 重啟後資料不丟失 4.redis 的所有操作都是原子性的,還支援對幾個操作...