首頁  >  文章  >  資料庫  >  聊聊Redis資料結構中的String類型

聊聊Redis資料結構中的String類型

青灯夜游
青灯夜游轉載
2021-12-08 09:52:491791瀏覽

這篇文章帶大家一起了解Redis資料結構中的String類型,並聊聊Redis的KV儲存結構,希望對大家有幫助!

聊聊Redis資料結構中的String類型

Redis常用作分散式KV緩存,許多人只會使用,卻不知道底層卻有著許多不為人知的秘密。 【相關推薦:Redis影片教學

String類型

String作為Redis支援的最基礎的資料類型,首先我們來看看String,他的資料結構和儲存是怎麼樣的。

重新定義SDS 去儲存String

眾所周知,redis是用c語言進行寫的,而c語言是沒有String類型的,只有char[],並且在初始化的是時候就必須大小指定型別後就不能改變。為了實現動態增加和擴充等功能,如incr指令,append指令,所以redis就自己定義維護了一個SDS(Simple Dynamic String)來實作這些功能。

我們先來看一下redis原始碼中定義的資料結構,這裡有5種類型,目的是為了節省空間。

聊聊Redis資料結構中的String類型

1、len:取得char[]的長度,需要遍歷數組,len(char[])時間複雜度O(n);
2、alloc :c語言沒有String類型, 只有char[],且char[]必須先分配空間長度,char[]預先分配了長度,資料增長後需要擴充;

3、falgs:總是佔用一個位元組.其中的最低3個bit用來表示header的型別。 header的類型共有5種,在sds.h有常數定義。
4、buf[]:c語言的char數組,用'\0'代表結束,意味著儲存二進位資料不能包含'\0',圖片音訊等用二進位儲存會有問題-這就是為什麼Redis說自己實作的SDS是二進位安全的字串。

SDS對c原始char數組的改進

1、Redis實作的SDS支援擴容
2、包含長度len,取得長度複雜度O(1 )
3、空間預先分配
4、惰性空間釋放(下面會講)

#SDS的優缺點

##優點

    能夠支援擴容
  • 包含長度len,取得長度複雜度O(1)
  • #空間預先分配
缺點

    #需要分配額外的記憶體
  • 頻繁的分配和回收帶來的效率問題

#Redis 所使用的記憶體分配庫jemalloc

jemalloc 在分配記憶體時,會根據我們申請的位元組數N,找一個比N 大,但是最接近N 的2 的冪次數作為分配的空間,這樣可以減少頻繁分配的次數。舉個例子。如果你申請 6 個位元組空間,jemalloc 實際上會分配 8 個位元組空間;如果你申請 24 個位元組空間,jemalloc 則會分配 32 個位元組。所以,在我們剛剛說的場景裡,dictEntry 結構就佔了 32 個位元組。

空間預先分配

空間預先分配用於最佳化SDS 的字串成長操作: 當SDS 的API 對一個SDS 進行修改, 並且需要對SDS 進行空間擴充的時候, 程式不僅會為SDS 分配修改所必須要的空間, 還會為SDS 分配額外的未使用空間。

其中, 額外分配的未使用空間數量由以下公式決定:

    如果對SDS 進行修改之後, SDS 的長度(也即是len 屬性的值)將小於1 MB , 那麼程式指派和len 屬性同樣大小的未使用空間, 這時SDS len 屬性的值將會和free 屬性的值相同。舉個例子, 如果進行修改之後, SDS 的len 將變成13 字節, 那麼程式也會分配13 個位元組的未使用空間, SDS 的buf 陣列的實際長度將變成13 13 1 = 27 位元組(額外的一位元組用於保存空字元)。
  • 如果對 SDS 進行修改之後, SDS 的長度將大於等於 1 MB , 那麼程式會分配 1 MB 的未使用空間。舉個例子, 如果進行修改之後, SDS 的 len 將變成 30 MB , 那麼程式會分配 1 MB 的未使用空間, SDS 的 buf 陣列的實際長度將為 30 MB 1 MB 1 byte 。
透過空間預先分配策略, Redis 可以減少連續執行字串成長運算所需的記憶體重新分配次數。

惰性釋放

惰性空間釋放用於最佳化SDS 的字串縮短操作: 當SDS 的API 需要縮短SDS 保存的字串時, 程式並不立即使用記憶體重新分配來回收縮短後多出來的位元組, 而是使用free 屬性將這些位元組的數量記錄起來, 並等待將來使用。

Redis的KV儲存結構

在redis中,所有的儲存都是以KV鍵值對的形式儲存的,K是字串類型,就是SDS;V 可能是字串、list、hash等(Redis支援的資料結構),V並沒有直接定成具體的類型,而是用redisObject封裝了一層;實際儲存的資料結構是由ptr指標具體指向。

並且,redis為了更好的節省空間,ptr指針也有不同方式的存儲,一方面,當保存的是Long 類型整數時,RedisObject 中的指針就直接賦值為整數數據了,這樣就不用額外的指針再指向整數了,節省了指針的空間開銷。另一方面,當保存的是字串數據,且字串小於等於 44 個位元組時,RedisObject 中的元數據、指標和 SDS 是一塊連續的記憶體區域,這樣就可以避免記憶體碎片。這種佈局方式也被稱為 embstr 編碼方式。當然,當字串大於 44 位元組時,SDS 的資料量就開始變多了,Redis 就不再把 SDS 和 RedisObject 佈局在一起了,而是會給 SDS 分配獨立的空間,並用指標指向 SDS 結構。這種佈局方式稱為 raw 編碼模式。如圖所示

聊聊Redis資料結構中的String類型

  • embstr 編碼
    儲存簡短字串,一次的記憶體分配;
    它是唯讀的,如果對內容進行修改,就會變成raw編碼(即使沒超過44位元組);
  • raw 編碼
    可分配多次記憶體空間,儲存大於44個位元組的長字串。

raw 原生SDS 字元長度 縮減到小於44,會逆向變成embstr編碼嗎?
不會;Redis底層編碼,轉變後 不可逆(不會回退)。

總結

redis是常用的快取中間件,我們必須了解清楚他的資料結構和存儲,以便以使用的時候的選擇更加合適的資料結構和內存的預估。

redis記憶體計算位址 http://www.redis.cn/redis_memory/

更多程式相關知識,請造訪:程式設計入門! !

以上是聊聊Redis資料結構中的String類型的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除