2014了休息了10多天沒有寫博客了不能把剛剛培養的習慣又荒廢了那麼就開始新年新旅途吧數據庫
Sds Simple Dynamic String簡單動態字符串是Redis 底層所使用的字符串表示它被用
在幾乎全部的Redis 模塊中。
本章將對sds 的實現、性能和功能等方面進行介紹並說明Redis 使用sds 而不是傳統C 字符
串的緣由。數組
Sds 在Redis 中的主要做用有如下兩個緩存
一、 實現字符串對象StringObject安全
Redis 是一個鍵值對數據庫key-value DB數據庫的值能夠是字符串、集合、列表等多種類
型的對象而數據庫的鍵則老是字符串對象。服務器
在Redis 中
一個字符串對象除了能夠保存字符串值以外還能夠保存long 類型的值因此爲了嚴謹起見
這裏須要強調一下當字符串對象保存的是字符串時它包含的纔是sds 值不然的話它就
是一個long 類型的值。app
舉個例子如下命令建立了一個新的數據庫鍵值對這個鍵值對的鍵和值都是字符串對象它
們都包含一個sds 值ide
如下命令建立了另外一個鍵值對它的鍵是字符串對象而值則是一個集合對象性能
2.、在Redis 程序內部用做char* 類型的替代品優化
在Redis 中客戶端傳入服務器的協議內容、aof 緩存、返回給客戶端的回覆等等這些重要的內容都是由都是由sds 類型來保存的。對象
在C 語言中字符串能夠用一個\0 結尾的char 數組來表示。
好比說hello world 在C 語言中就能夠表示爲"hello world\0" 。
這種簡單的字符串表示在大多數狀況下都能知足要求可是它並不能高效地支持長度計算和
追加append這兩種操做
每次計算字符串長度strlen(s)的複雜度爲(N) 。
對字符串進行N 次追加一定須要對字符串進行N 次內存重分配realloc。
考慮到這兩個緣由Redis 使用sds 類型替換了C 語言的默認字符串表示sds 既能夠高效地
實現追加和長度計算而且它仍是二進制安全的。
sds的實現由如下兩個部分組成
typedef char *sds; struct sdshdr{ //buf已佔用長度 int len; //buf剩餘可用長度 int free; //實際保存字符串數據的地方 char buf[]; };
做爲例子如下是新建立的一樣保存hello world 字符串的sdshdr 結構
struct sdshdr { len = 11; free = 0; buf = "hello world\0"; // buf 的實際長度爲len + 1 };
經過len 屬性sdshdr 能夠實現複雜度爲(1) 的長度計算操做。
另外一方面經過對buf 分配一些額外的空間並使用free 記錄未使用空間的大小sdshdr 可
以讓執行追加操做所需的內存重分配次數大大減小。
爲了易於理解咱們用一個Redis 執行實例做爲例子解釋一下當執行如下代碼時Redis
內部發生了什麼
首先SET 命令建立並保存hello world 到一個sdshdr 中這個sdshdr 的值以下
struct sdshdr { len = 11; free = 0; buf = "hello world\0"; }
當執行APPEND 命令時相應的sdshdr 被更新字符串" again!" 會被追加到原來的
"hello world" 以後
struct sdshdr { len = 18; free = 18; // 空白的地方爲預分配空間共18 + 18 + 1 個字節 buf = "hello world again!\0 "; }
注意當調用SET 命令建立sdshdr 時sdshdr 的free 屬性爲0 Redis 也沒有爲buf 建立
額外的空間——而在執行APPEND 以後Redis 爲buf 建立了多於所需空間一倍的大小。
在這個例子中保存"hello world again!" 共須要18 + 1 個字節但程序卻爲咱們分配了
18 + 18 + 1 = 37 個字節——這樣一來若是未來再次對同一個sdshdr 進行追加操做只要
追加內容的長度不超過free 屬性的值那麼就不須要對buf 進行內存重分配。
好比說執行如下命令並不會引發buf 的內存重分配由於新追加的字符串長度小於18
再次執行APPEND 命令以後msg 的值所對應的sdshdr 結構能夠表示以下
struct sdshdr { len = 25; free = 11; // 空白的地方爲預分配空間共18 + 18 + 1 個字節 buf = "hello world again! again!\0 "; }
在目前版本的Redis 中SDS_MAX_PREALLOC 的值爲1024 * 1024 也就是說當大小小於
1MB 的字符串執行追加操做時sdsMakeRoomFor 就爲它們分配多於所需大小一倍的空間當
字符串的大小大於1MB 那麼sdsMakeRoomFor 就爲它們額外多分配1MB 的空間。
Note: 這種分配策略會浪費內存嗎
執行過APPEND 命令的字符串會帶有額外的預分配空間這些預分配空間不會被釋放除非
該字符串所對應的鍵被刪除或者等到關閉Redis 以後再次啓動時從新載入的字符串對象將
不會有預分配空間。
由於執行APPEND 命令的字符串鍵數量一般並很少佔用內存的體積一般也不大因此這一
般並不算什麼問題。
另外一方面若是執行APPEND 操做的鍵不少而字符串的體積又很大的話那可能就須要修
改Redis 服務器讓它定時釋放一些字符串鍵的預分配空間從而更有效地使用內存。
小結
Redis 的字符串表示爲sds 而不是C 字符串以\0 結尾的char*。 對比C 字符串sds 有如下特性– 能夠高效地執行長度計算strlen– 能夠高效地執行追加操做append– 二進制安全 sds 會爲追加操做進行優化加快追加操做的速度並下降內存分配的次數代價是多佔用了一些內存並且這些內存不會被主動釋放。