共計 2388 個字符,預計需要花費 6 分鐘才能閱讀完成。
這篇文章主要介紹“Redis 數據結構中的 String 類型有哪些”,在日常操作中,相信很多人在 Redis 數據結構中的 String 類型有哪些問題上存在疑惑,丸趣 TV 小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Redis 數據結構中的 String 類型有哪些”的疑惑有所幫助!接下來,請跟著丸趣 TV 小編一起來學習吧!
Redis 常用作分布式 KV 緩存,很多人僅僅只會使用,卻不知道底層卻有著很多不為人知的秘密。
String 類型
String 作為 Redis 支持的最基礎的數據類型,首先我們來看下 String,他的數據結構和存儲是怎么樣的。
重新定義 SDS 去存儲 String
眾所周知,redis 是用 c 語言進行編寫的,而 c 語言是沒有 String 類型的,只有 char[],并且在初始化的是時候就必須大小指定類型后就不能改變。為了實現動態增加和擴展等功能,如 incr 命令,append 命令,所以 redis 就自己定義維護了一個 SDS(Simple Dynamic String)來實現這些功能。
我們先來看一下 redis 源碼中定義的數據結構, 這里有 5 種類型,目的是為了節省空間。
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 編碼模式。如圖所示
embstr 編碼
存儲簡短字符串,一次的內存分配;
它是只讀的,如果對內容進行修改,就會變成 raw 編碼(即使沒超過 44 字節);
raw 編碼
可分配多次內存空間,存儲大于 44 個字節的長字符串。
raw 原生 SDS 字符長度 縮減到小于 44,會逆向變成 embstr 編碼嗎?
不會;Redis 底層編碼,轉變后 不可逆(不會回退)。
到此,關于“Redis 數據結構中的 String 類型有哪些”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注丸趣 TV 網站,丸趣 TV 小編會繼續努力為大家帶來更多實用的文章!