redis-簡單動態字符串(sds)和鏈表的底層實現

1、簡單動態字符串(simple dynamic string,SDS)

1.SDS的定義

struct sdshdr{
        //記錄buf數組中已使用字節的數量
        //等於SDS所保存字符串的長度
        int len;
        
        //記錄buf數組中未使用字節的數量
        int free;
        
        //字節數組,用於保存字符串
        char buf[];
    }
複製代碼

2.SDS的優勢

  • 獲取自身長度爲O(1),直接取len字段。
  • 杜絕緩衝區溢出,當SDS的API須要對SDS進行修改時,API會先檢查SDS的空間是否知足修改的所需的要求,若是不知足的話,API會自動將SDS的空間擴展至執行修改所需的大小,而後才執行實際的修改操做,因此使用SDS既不須要手動修改SDS的空間大小,也不會出現前面所說的緩衝區溢出問題。
  • 減小修改字符串時帶來的內存重分配次數,舉個例子,當len長度爲20,free長度爲0,buf[]中算上空字符(\0)一共21個字符。當減小字符串時,好比截取前10個,此時len長度爲10,free中未使用的長度爲10,buf[]的空間長度仍然是21,未使用的空間未延遲釋放,這樣若是在增長修改此字符串時,若是增長的長度不大於10則不會新增分配空間。而增長爲redis修改字符串的速度。
  • 空間預分配,若是對SDS進行修改後,SDS的長度小於1MB,那麼len和free數量相同,舉例,若是修改以後,SDS的len變成13字節,那麼程序會分配13字節未使用空間,此時buf實際長度爲13+13+1=27字節(額外的一字節用於保存空字符);SDS修改後的長度大於1MB,則free固定分配1MB,舉例,若是修改以後,SDS的len變成30MB,那麼程序會分配1MB未使用空間,此時buf實際長度爲30MB+1MB+1byte
  • 二進制安全,爲了確保redis能夠適用於各類不一樣的使用場景,全部SDS API都會以處理二進制的方式來處理SDS存放在buf數組裏的數據,程序不會對其中的數據作任何限制,過濾,或者假設,數據在寫入時是什麼樣的,它被讀取時就是什麼樣。這也是咱們將SDS的buf屬性稱爲字節數組的緣由-redis不是用這個數組來保存字符,而是用它來保存一系列二進制數據。

2、鏈表

1.鏈表和鏈表節點的實現

typedef struct listNode {
    
    //前置節點
    struct listNode *prev;
    
    //前置節點
    struct listNode *next;
    
    //節點的值
    void *value;
}listNode;

typedef struct list {
    
    //表頭節點
    listNode *head;
    
    //表尾節點
    listNode *tail;
    
    //鏈表所包含的節點數量
    unsigned long len;
    
    //節點值複製函數
    void *(*dup) (void *ptr);
    
    //節點值釋放函數
    void *(*free) (void *ptr);
    
    //節點值釋放函數
    void *(*match) (void *ptr,void *key);
}list;

複製代碼

2.redis的鏈表實現的特性總結以下

  • 雙端:鏈表節點帶有prev和next指針,獲取某個節點的前置節點和後置節點的複雜度都是O(1)。
  • 無環:表頭節點的prev指針和表尾節點的next指針都指向NULL,對鏈表的訪問以NULL爲終點。
  • 帶表頭指針和表尾指針:經過list結構的head指針和tail指針,程序獲取連接表的表頭節點和表尾節點的複雜度爲O(1)。
  • 帶鏈表長度計數器:程序使用list結構的head指針和tail指針,程序獲取鏈表的表頭節點和表尾節點的複雜度爲O(1)。
  • 多態:鏈表節點使用使用void*指針來保存節點值,而且能夠經過list結構的dup、free、match三個屬性爲節點值設置類型特定函數,因此鏈表能夠用於保存各類不一樣類型的值
相關文章
相關標籤/搜索