redis 是基於 c 語言的記憶體資料庫,但是 redis 中並沒有使用 c 語言的字串(實質是 以空格結尾的字元陣列)作為預設的字串,而是自己構建了一種名為簡單動態字串(****** dynamic string)的抽象資料結構,將其用作預設的字串表示。
通常而言,sds 在 redis 中被用於 1. 預設的字串結構;2. 用作緩衝區(buffer)。
我們首先看看 redis 是如何定義 sds 的,再看看為什麼要通過實現 sds 作為預設的字串實現。
在 redis 在sds.h/sdshrd
定義了 sds 的結構體:
struct
下圖是乙個 sds 的示例:
len
儲存了buf
中儲存字串的實際長度
free
是buf
中剩餘空閒空間
在 c 語言中,獲取字串(也就是字元陣列)的長度,是乙個 o(n) 複雜度的操作。
sds 的len
欄位令 redis 獲取字串長度的複雜度降為 o(1)(直接讀取len
字段即可),這也確保了「獲取字串長度」這一行為,不會成為 redis 的效能瓶頸。
什麼是「緩衝區溢位」?首先,我們需要知道的是,c 語言通過空字元
\0
判斷字串是否結束。
如下例,在記憶體中有下面兩個連續的 c 字串s1
和s2
,各自儲存了redis
和str
兩個字串:
c 在執行字串拼接函式strcat
時,會假定呼叫者已經分配好了足夠的記憶體、直接在字串末尾新增對應字元。
即,如果我們在上圖情況下,執行strcat(s1, "01")
,便會得到下圖的結果:
可以看到,s1
雖然正常修改,但其資料溢位到s2
所在的記憶體空間中,導致s2
被意外修改了:s2
變成了字串"1"
。
這便是我們所說的 緩衝區溢位。
sds 如何杜絕?sds 通過它的空間分配策略杜絕「緩衝區溢位」這一現象。
當通過 sds 的 api 對乙個 sds 進行修改時,api 會按照下列步驟執行:
檢查 sds 的空間是否滿足要求
如果不滿足要求,api 會先對 sds自動進行擴容
執行實際的修改操作
前面提到,如果通過 api 對 sds 進行修改,api 會通過自動擴容來確保 sds 空間滿足要求;這是 sds 空間分配策略的一種,這裡我們仔細說一下 sds 具體的空間分配策略。
c 字串的空間分配sds 的空間分配策略和 c 不同,對於 c 字串,其空間分配策略如下:
如果需要進行的是「縮短字串」,例如截斷操作 trim
可以看到,每次對 c 字串進行操作時,都需要進行 記憶體重分配。
基於這個原因,sds 實現了自己的「空間分配策略」。
sds 的空間分配策略對於每個 sds 結構,
buf
陣列長度和實際儲存的字串長度並不是相同的,準確地說,應該是len(buf)==len+free+1
(buf
陣列的空字元不在len
和free
計算中)。sds 基於free
字段實現了「空間預分配」和「惰性空間釋放」兩種空間分配策略。
空間預分配
空間預分配用於優化字串的增長操作。
當 sds api 對 sds 字串進行增長時,不僅會為 sds 分配修改所需空間,還會基於 sds 字串的增長後的實際長度(也就是len
)為 sds分配額外的空間。
如果增長後,sds 儲存的字串大於等於 1mb
如下圖,有 sds"redis"
,我們對其順序執行兩次修改:
第一次追加字串"01"
當前free
為 1,小於字串"01"
的長度,因此需要擴容,也就是進行記憶體重分配
因為追加後的len
為 7,小於 1024,因此我們直接擴充未使用空間free
到len
的大小,也就是 7
第二次追加字串"02"
當前free
為 7,足夠追加字串"02"
,因此無需擴容,直接追加即可
可以看到,通過這種「空間預分配」的策略,sds 將連續增長 n 次字串所需的記憶體分配次數從「必定 n 次」優化為「最多 n 次」。
惰性空間釋放
惰性空間釋放用於優化字串的縮短操作。
當 sds api 需要縮短 sds 儲存的字串時,程式不會像 c 一樣立即釋放被縮短的空間,而是僅增長free
字段、並將空間保留至將來使用。
如下圖,我們縮短之前的 sds"redis0102"
至"redis"
:
這樣做的好處是,如果後續有對這個字串進行增長操作時,可以盡可能的減少擴容動作。
與此同時,sds 也提供了「主動釋放空閒記憶體」的 api,讓我們在有需要時讓這些空閒空間真正被釋放,防止記憶體浪費。
在 c 語言中,由於是用字元陣列實現的字串,安全性方面天然就帶有一些限制:
這也使得 c 語言的字串只能儲存文字資料,不能儲存、音訊等二進位制資料,也就是「二進位制不安全」。
而為什麼說 sds 是二進位制安全的呢?
因此,sds 是乙個「二進位制安全」的字串,它可以儲存任何形式的二進位制資料。
如前文「sds 的定義」一節中所說,sds 和 c 語言一樣,通過字元陣列儲存字串,且末尾有乙個空字元\0
作為結尾。這樣儲存文字資料的 sds 就可以復用部分中的函式了。
最後再對 sds 做乙個總結。
sds 是 redis 在 c 字串基礎上實現的資料結構,用於 redis 中預設的字串表示,其結構體定義如下:
struct
相比於原生的 c 字串,sds 有如下的優點: Redis資料結構 SDS(簡單動態字串)
redis中所有字串都是用sds 簡單動態字串 實現的,該結構體內部定義如下 struct sdshdr 關於sds的特點,可以總結為以下幾點 1.sds遵循c字串以空字元結尾的風格,相容部分c字串函式,buf在末尾缺省會帶上乙個 0 字元,但是不會計算到len欄位裡面,因此buf的實際大小為len...
Redis資料結構 簡單的動態字串
c語言中的字串是基於陣列來實現的,每個字串會多出乙個空字元 redis中的簡單動態字串是sds dynamic string 型別 sds遵循c語言中以空字元為結尾的慣例是為了利用c語言函式庫中的某些字串函式,而且新增空字元的這乙個操作是有sds函式自動完成的。redis採用sds的原因是為了滿足字...
Redis資料結構 簡單動態字串SDS
最新 redis記憶體 三個重要的緩衝區 最新 redis記憶體 記憶體消耗 記憶體都去哪了?最新 redis持久化 如何選擇合適的持久化方式 最新 redis持久化 aof日誌 相信用過redis的人都知道,redis提供了乙個邏輯上的物件系統構建了乙個鍵值對資料庫以供客戶端使用者使用。這個物件系...