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三個屬性爲節點值設置類型特定函數,因此鏈表能夠用於保存各類不一樣類型的值