SDS
特性
1、常数时间获取长度
2、杜绝缓冲区溢出,当连接字符串后长度超过buf实际长度,会预先扩展空间。
3、空间预分配。当对SDS修改并且需要扩展空间时,SDS为预分配双倍字符串长度的空间(len < 1MB)。例如,当前sds长度为13,修改后len = 13,free = 13,buf实际长度为13 + 13 + 1 = 27(包括\0)。如果sds修改后len大于等于1MB,程序则分配1MB的未使用空间。例如,修改后len = 30MB,则buf的时间长度为30MB + 1MB + 1byte。
4、二进制安全,不以\0区分是否字符串结尾,而以len判断
5、兼容部分C字符串函数
Linked List
每个链表节点使用adlist.h/listNode表示。
typedef struct listNode {
struct listNode *prev;
struct listNode *next;
void *value;
} listNode;
多个listNode组成链表list:
typedef struct list {
listNode *head;
listNode *tail;
void *(*dup)(void *ptr);
void (*free)(void *ptr);
int (*match)(void *ptr, void *key);
unsigned long len;
} list;
其中,dup函数用于复制链表节点所保存的值,free函数用于释放链表节点所保存的值,match函数用于对比链表节点所保存值是否与另一输入值相等。
Dict
Redis字典所使用的哈希表由dict.h/dictht结构定义:
typedef struct dictht {
dictEntry **table; /* 哈希表数组 */
unsigned long size; //哈希表大小
unsigned long sizemask; // 哈希表大小掩码,用于计算索引,总是等于size - 1
unsigned long used; // 该哈希表已有节点的数量
} dictht;
table属性是一个数组,每个元素都是指向dict.h/dictEntey结构的指针。
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next; // 指向下个哈希节点,形成链表
} dictEntry;
由结构可知,redis使用拉链法处理冲突。
Redis字典由dict.h/dict表示
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
unsigned long iterators; /* number of iterators currently running */
} dict;
type属性和privdata属性是针对不同类型的键值对,为创建多态字典而设置的:
- type是指向dictType结构的指针,每个dictType保存一簇用于操作特定类型键值对的函数,Redis会为用途不同的字典设置不同的类型特定函数。
- privdata保存需要传给那些特定类型特定函数的可选参数。
typedef struct dictType {
uint64_t (*hashFunction)(const void *key);
void *(*keyDup)(void *privdata, const void *key);
void *(*valDup)(void *privdata, const void *obj);
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
void (*keyDestructor)(void *privdata, void *key);
void (*valDestructor)(void *privdata, void *obj);
} dictType;
ht属性是一个包含两个哈希表的数组,一般情况下,字典只是用ht[0],ht[1]只在rehash时使用。
rehashidx记录rehash进度,未在rehash时,值为-1。
rehash
随着操作的不断执行,哈希表保存的键值对会逐渐地增多或者减少,为了让哈希表的负载因子维持一个合理范围,当哈希表保存的kv太多或者太少时,程序需要对哈希表进行相应扩展或收缩。
rehash步骤如下:
- 为字典ht[1]分配空间,空间大小取决于要执行的操作以及ht[0]当前的kv数量。如果是
- 如果执行的是扩展操作,ht[1]大小为第一个大于等于ht[0].used * 2的2^n;
- 如果执行的是收缩操作,ht[1]大小为第一个大于等于ht[0].used的2^n;
- 将保存在ht[0]中所有键值对reshash到ht[1]上面;rehash指的是重新计算键的哈希值和索引值,然后将 键值对放置到ht[1]哈希表指定位置。
- 所有kv迁移完毕后,释放ht[0],将ht[1]设置为ht[0]。