(三分鐘系列)詳解Redis字串內部結構

spacedong發表於2018-09-10

前言

Redis 中有五種資料型別,分別是 Str (字串)、 Hash (雜湊)、 List (列表)、 Set (集合)、 Zset (有序集合)。 這五種資料型別的實際運用與底層實現和其他語言中的資料結構的實現有所不同,底層實現是由Redis基於C語言的基礎上來完成的。接下來我們會結合原始碼來討論 Redis 字串中的實際運用與底層原理。

運用範例

redis 127.0.0.1:6379> SET name "spacedong"
OK
redis 127.0.0.1:6379> GET name
"spacedong"
複製程式碼
  • 一個String鍵能夠儲存512M的資料。
  • String型別是二進位制安全的,可以儲存的資料是任何形式的,比如序列化的物件,圖片等。
  • 範例中的鍵值對中的 name 這個鍵是一個 SDS 字串,"spacedong"也是一個 SDS 字串。

特性詳解

  • String 的字串底層是由動態字串(simple dynamic string ,SDS)來實現的。
struct sdshdr {

    // 記錄 buf 陣列中已使用位元組的數量
    // 等於 SDS 所儲存字串的長度
    int len;

    // 記錄 buf 陣列中未使用位元組的數量
    int free;

    // 位元組陣列,用於儲存字串
    char buf[];

};
複製程式碼
  • SDS 字串中的結構

image

  • free 是指在 SDS 中未使用的空閒字元空間,如圖為 5
  • len 是在 SDS 中實際使用的長度,如圖為 5
  • 在 SDS 中是會額外申請一個空字元來儲存結尾的字元,這個和C語言中的字串是一樣。
  • 獲取 SDS 的字串長度

在 SDS 中獲取實際儲存字串的長度方法(原始碼如下)

/*
 * 
 * 返回 sds 實際儲存的字串的長度
 *
 * T = O(1)
 */
static inline size_t sdslen(const sds s) {
    struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
    return sh->len;
}
複製程式碼

** 在 SDS 中獲取未使用空間的方法**(原始碼如下

/*
 * 返回 sds 可用空間的長度
 *
 * T = O(1)
 */
static inline size_t sdsavail(const sds s) {
    struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
    return sh->free;
}
複製程式碼
  • SDS 中防止緩衝區溢位的策略

C語言中的字串在擴容的時候,已經假設為該字串分配了足夠的記憶體空間了。如果沒有分配的話,那麼所以當有需要擴容的字串時會產生緩衝區溢位,在 SDS 中的則是在每次擴容的時候先進行判斷,是否有足夠的空間來儲存傳進來的字串,如果沒有,則是擴容到相對應的長度,同時對 free 屬性分配相對應的長度。

例如一開始的使用長度為 5 ,需要擴容的長度為 5 ,那麼free分配的長度就為10。這個是與 SDS 的空間分配策略有關。

- SDS 修改字串時策略及好處

在字串進行修改的時候,會涉及到記憶體的重分配,這是一個複雜的操作,有可能涉及到系統的呼叫,SDS 在修改字串的時候的策略可以為減少記憶體重分配的次數。

1. 空間預分配策略

具體的策略是在字串擴容時,會在擴容後為 free 屬性分配一個合適的長度,這樣的目的是為下次的字串擴容的時候提前分配好了一定的記憶體。

這裡的合適長度是指:如果 SDS 字串的長度為小於1M,那麼分配和實際使用長度相同的記憶體,如修改後的字串長度為13個字元,那麼為 free 分配 13 個字元的記憶體,如果修改後的 SDS 長度大於 30M,那麼為 free 分配的長度為 1M 的記憶體。

2. 惰性空間策略

如果 SDS 字串的長度在修改後變短的話,那麼空餘出來的長度分配給 free ,為的是下次的字串的使用。

3. 好處

可以讓記憶體分配的次數從一定為 N 次變為最多是 N 次。同時也可以滿足 redis 中的對資料頻繁修改的要求。

- 二進位制的安全資料

在 SDS 中存的是二進位制的資料格式,這種格式的資料存進來是什麼樣的,取出來的時候也是怎麼樣的,同時也是滿足儲存多種資料格式的資料的要求,如儲存圖片,文字資料等。

相關文章