sds結構體組成:struct sdshdr ;
sds相比普通字串的好處:
redis 只會使用c字串作為字面量,在大多數情況下,redis 使用sds(****** dynamic string,簡單動態字串)作為字串表示。
比起 c 字串, sds 具有以下優點:
* 常數複雜度獲取字串長度。
* 杜絕緩衝區溢位。
* 減少修改字串長度時所需的記憶體重分配次數。
* 二進位制安全。
* 相容部分 c 字串函式。
redis自己構建了一種名為簡單動態字串(****** dynamic string,sds)的抽象型別, 並將 sds 用作 redis 的預設字串表示。
c 字串只會作為字串字面量(string literal), 用在一些無須對字串值進行修改的地方, 比如列印日誌。
當 redis 需要乙個可以被修改的字串值時,redis 就會使用 sds 來表示字串值: 比如在 redis 的資料庫裡面, 包含字串值的鍵值對在底層都是由 sds 實現的。
當然也可以通過s->buf來直接訪問字串,比如用printf來輸出
c 字串並不記錄自身的長度資訊, 所以為了獲取乙個 c 字串的長度, 程式必須遍歷整個字串, 對遇到的每個字元進行計數, 直到遇到代表字串結尾的空字元為止, 這個操作的複雜度為 o(n)
sds記錄自身長度資訊,獲取乙個 sds 長度的複雜度僅為 o(1) 。
設定和更新 sds 長度的工作是由 sds 的 api 在執行時自動完成的, 使用 sds 無須進行任何手動修改長度的工作。
通過使用 sds 而不是 c 字串, redis 將獲取字串長度所需的複雜度從 o(n) 降低到了 o(1) , 這確保了獲取字串長度的工作不會成為 redis 的效能瓶頸。
c 字串不記錄自身長度帶來的另乙個問題是容易造成緩衝區溢位,比如在執行字串拼接的操作時候,就容易發生溢位的現象。
sds 的空間分配策略完全杜絕了發生緩衝區溢位的可能性: 當 sds api 需要對 sds 進行修改時, api 會先檢查 sds 的空間是否滿足修改所需的要求, 如果不滿足的話, api 會自動將 sds 的空間擴充套件至執行修改所需的大小, 然後才執行實際的修改操作, 所以使用 sds 既不需要手動修改 sds 的空間大小, 也不會出現前面所說的緩衝區溢位問題
因為 c 字串並不記錄自身的長度,所以對於乙個包含了n個字元的c字串來說,這個c字串的底層實現總是乙個 n+1 個字元長的陣列
c 字串的長度和底層陣列的長度之間存在著這種關聯性, 所以每次增長或者縮短乙個 c 字串, 程式都總要對儲存這個 c 字串的陣列進行一次記憶體重分配操作:
如果程式執行的是縮短字串的操作, 比如截斷操作(trim), 那麼在執行這個操作之後, 程式需要通過記憶體重分配來釋放字串不再使用的那部分空間 —— 如果忘了這一步就會產生記憶體洩漏。
因為記憶體重分配涉及複雜的演算法, 並且可能需要執行系統呼叫, 所以它通常是乙個比較耗時的操作:為了避免 c 字串的這種缺陷, sds 通過未使用空間解除了字串長度和底層陣列長度之間的關聯: 在 sds 中, buf 陣列的長度不一定就是字元數量加一, 陣列裡面可以包含未使用的位元組, 而這些位元組的數量就由 sds 的 free 屬性記錄。在一般程式中, 如果修改字串長度的情況不太常出現, 那麼每次修改都執行一次記憶體重分配是可以接受的。
但是 redis 作為資料庫, 經常被用於速度要求嚴苛、資料被頻繁修改的場合, 如果每次修改字串的長度都需要執行一次記憶體重分配的話, 那麼光是執行記憶體重分配的時間就會占去修改字串所用時間的一大部分, 如果這種修改頻繁地發生的話, 可能還會對效能造成影響。
通過未使用空間, sds 實現了空間預分配和惰性空間釋放兩種優化策略。
通過空間預分配策略, redis 可以減少連續執行字串增長操作所需的記憶體重分配次數。
在擴充套件 sds 空間之前, sds api 會先檢查未使用空間是否足夠, 如果足夠的話, api 就會直接使用未使用空間, 而無須執行記憶體重分配。
通過這種預分配策略, sds 將連續增長 n 次字串所需的記憶體重分配次數從必定 n 次降低為最多 n 次。
惰性空間釋放用於優化 sds 的字串縮短操作: 當 sds 的 api 需要縮短 sds 儲存的字串時, 程式並不立即使用記憶體重分配來**縮短後多出來的位元組, 而是使用 free 屬性將這些位元組的數量記錄起來, 並等待將來使用。
通過惰性空間釋放策略, sds 避免了縮短字串時所需的記憶體重分配操作, 並為將來可能有的增長操作提供了優化。
與此同時, sds 也提供了相應的 api , 讓我們可以在有需要時, 真正地釋放 sds 裡面的未使用空間, 所以不用擔心惰性空間釋放策略會造成記憶體浪費。
雖然資料庫一般用於儲存文字資料, 但使用資料庫來儲存二進位制資料的場景也不少見, 因此, 為了確保 redis 可以適用於各種不同的使用場景, sds 的 api 都是二進位制安全的(binary-safe): 所有 sds api 都會以處理二進位制的方式來處理 sds 存放在 buf 陣列裡的資料, 程式不會對其中的資料做任何限制、過濾、或者假設 —— 資料在寫入時是什麼樣的, 它被讀取時就是什麼樣。
這也是我們將 sds 的 buf 屬性稱為位元組陣列的原因 —— redis 不是用這個陣列來儲存字元, 而是用它來儲存一系列二進位制資料。
通過使用二進位制安全的 sds , 而不是 c 字串, 使得 redis 不僅可以儲存文字資料, 還可以儲存任意格式的二進位制資料。
雖然 sds 的 api 都是二進位制安全的, 但它們一樣遵循 c 字串以空字元結尾的慣例: 這些 api 總會將 sds 儲存的資料的末尾設定為空字元, 並且總會在為 buf 陣列分配空間時多分配乙個位元組來容納這個空字元, 這是為了讓那些儲存文字資料的 sds 可以重用一部分 庫定義的函式。
redis資料結構 SDS
在使用中,redis有五種物件 string hash list set sorted set 在redis中有以下幾種資料結構 sds 鍊錶 字典 跳躍表 整數集合 壓縮列表,它們在不同的條件下實現了redis的五種物件。先來看sds的結構 在sds.h中定義了幾種不同的結構用來存放不同型別的資料...
Redis資料結構 一 SDS
redis沒有直接使用c語言傳統的字串表示 空字元結尾的字元陣列 而是自己構建了一種名為簡單動態字串的抽象型別,並將sds用作redis的預設字串表示 在redis裡面,c字串只會作為字串字面量,用在一些無須對字串值進行修改的地方,比如列印日誌.但當可能會修改字串值時,就需要sds來實現字串,比如,...
有趣的Redis 資料結構詳解 sds
我們知道redis是乙個鍵值對資料庫,當你執行如下命令時 set testkey testvalue 其中的鍵就是用sds dynamic string,簡單動態字串 來實現的,redis中string類的值也是用sds實現的 如上面的testvalue 我們來看一下sds的底層資料結構是啥?str...