redis是一个存储键值对的内存数据库,并且持久化到磁盘。
1、简单动态字符串(Simple Dynamic String,简称SDS)
1)利用len记录字符串长度,使得strlen时间复杂度从O(N)变为O(1)。
// sds.h struct __attribute__ ((__packed__)) sdshdr64 { uint64_t len; // 字符串已用的长度 uint64_t alloc; // 分配的总长度 char buf[]; // 字符串内容 };
2)利用类似vector的分配策略,append时预分配一倍空间,减少内存分配次数。
// sds.c/sdsMakeRoomFor if (avail >= addlen) return s; newlen = (len+addlen); if (newlen < SDS_MAX_PREALLOC) newlen *= 2; else newlen += SDS_MAX_PREALLOC; sdssetalloc(s, newlen);
2、双向链表(Doubly Linked List)
1)利用len,使得计算链表长度的时间复杂度从O(N)变为O(1)。
2)表头和表尾插入元素的时间复杂度都是O(1)。注:这是所有双向链表的特性。
// adlist.h typedef struct listNode { struct listNode *prev; struct listNode *next; void *value; } listNode; typedef struct list { listNode *head; listNode *tail; unsigned long len; // 链表长度 } list;
3、字典(Dictionary)
1)每个字典对应两个哈希表,ht[1]用于重哈希。
// 字典 typedef struct dict { dictht ht[2]; // 两个哈希表 long rehashidx; // 重哈希的进度,到哪个桶了 } dict;
2)解决冲突:哈希值一样的多个键值对,放在同一个链表,即对应同一个桶。
// 字典元素:键值对 typedef struct dictEntry { void *key; union { void *val; uint64_t u64; int64_t s64; double d; } v; struct dictEntry *next; } dictEntry; // 哈希表 typedef struct dictht { dictEntry **table; unsigned long size; // 桶个数 unsigned long sizemask; // 等于size - 1 unsigned long used; // 键值对个数 } dictht;
3)重哈希(rehash)
3.1)数据不是一次倒完,而是多次渐进。
3.2)新键值对会添加到ht[1]。