SDS
SDS的定義
在Redis中使用SDS(簡單動態字符串)來代替c風格字符串
struct SDS{
int len,free;
char buf[];
}
SDS與C風格字符串的區別
- O(1)獲取SDS長度
- 通過分析free來杜絕緩衝區溢出
- 通過空間預分配(大於1M分配1M,小於1M分配2倍)和惰性釋放(提供了內存釋放API)減少內存分配次數
- 二進制安全(因爲使用len而不是’\0’來判斷結束)
- SDS使用’\0’來結尾,所以可以複用部分C函數
鏈表
列表,慢查詢,發佈訂閱,監視器等功能中都用到了鏈表
Redis中鏈表的結構
typedef struct list{
listNode *head,*tail;//頭,尾
unsigned long len;//長度
void *(*dup)(void *ptr);//複製函數
void (*free)(void *ptr);//釋放函數
int (*match)(void *ptr,void *key);//匹配判斷函數
}list;
listNode 的底層是雙向無環鏈表
字典
Redis中數據庫,哈希表等功能的底層都用到了字典
字典的實現
typedef struct dictht{
dictEntry **table;//值數組
unsigned long size; // 哈希表大小
unsigned long sizemask;//掩碼
unsigned long used;//已使用數量
}
typedef struct dictEntry{
void *key;//鍵
union{//值
void *val;
uint64_t u64;
int64_t s64;
}v;
struct dictEntry *next;//下個哈希表節點
}dictEntry;
typedef struct dict{
dictType* type;
void *privatedata;
dictht ht[2];//維護兩個,爲了完成負載因子均衡
int trehashidx;//不在rehash時,值爲-1
}
在Redis中會根據不同的鍵類型,選擇不同的處理函數(type->hashFunction() , 如計算,處理,銷燬函數),Redis使用MH2算法.
解決鍵衝突
使用鏈地址法,在鏈的頭部插入新節點,保證插入的O1複雜度
處理負載因子
負載因子 = 保存節點數量 / size
在進行bgsave,bgrewrite時 觸發rehash的負載因子值爲5 , 不進行時爲 1 (因爲要避免寫時複製時多餘的內存寫入)
利用ht[1]來完成擴展(第一個大於used*2的2的冪)或者收縮(第一個大於used的2的冪) 空間大小
在大小改變後,將ht[0]中的元素rehash到ht[1]中,完成後交換兩者地址,完成rehash
漸進式rehash:每次訪問ht[0]時rehash一個rehashidx上的數據,當rehashidx等於達到上限時說明rehash完畢,將rehashidx置爲-1(通過漸進rehash避免服務長時間停止響應)
rehash 讀操作 會先在ht[0],後在ht[1]上讀 , 寫操作全部寫在ht[1]
跳錶
跳躍表支持平均logN,最壞o(N)的不穩定查找複雜度,可以通過順序性批量操作節點,是有序列表的底層實現之一,還是集羣內部的數據結構.
typedef struct zskiplistNode{
struct zskiplistNode *backward; // 後退指針
double score;//分數
robj *obj;//實際對象
struct zskiplistLevel{ //層
struct zskiplistLevel *forward; //每層上的前進指針
unsigned int span; // 跨度
}level[];//層的大小是1~32的隨機數
}zskiplistNode;