SDS 简单动态字符串
C语言字符串:以空字符’\0’结尾
SDS结构:
struct sdshdr {
int len;// 记录buf中已使用的字节数量,等于SDS字符串的长度
int free; // 记录buf中未使用的字节数量
char buf[]; // 字节数组,遵循用空字符结尾
}
与C字符串的区别
1. 获取字符串长度常数复杂度
2. 杜绝缓存区溢出,如果C字符串使用超过了分配的空间,则溢出
3. 减少字符串修改带来的内存重新分配次数(空间预分配——需要扩展空间时,小于1M,预分配一样大小的空闲,大于1M,预分配1M空闲,惰性释放,不会真的释放,而是放在free中,可以调用指定api释放)
4. 二进制安全,非以’\0’结尾
5. 兼容部分C字符串api
链表
列表键中包含了很多元素,或者列表中的元素都是很长的字符串,redis会使用链表来作为列表键的底层实现。除此之外,发布订阅,慢查询,监视器等也用到了链表。
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); // 释放函数
int (*match)(void *prt, void *key);// 对比函数
} list;
字典
redis数据库就是用字典实现的,也是哈希键的底层实现之一,如果一个哈希键包含较多元素或者值都是较长的字符串,就会使用字典实现。
typedef struct dictht {
// 哈希表数组
dictEntry **table;
// 哈希表大小
unsigned long size;
// 哈希表大小掩码,用于计算索引值,总是等于size - 1
unsigned long sizemask;
// 哈希表已有节点数量
unsigned long used;
} dictht;
typedef struct dictEntry {
void *key;
union {
void *value;
uint64_tu64;
int64_ts64;
} v;
struct dictEntry *next;
} dictEntry;
// 字典对象
typedef struct dict {
// 类型
dictType *type;
// 私有数据
void *privdata;
// 哈希表
dictht ht[2];
// rehash索引
int trehashidx; /*rehashing not in progress if trehashidx == -1*/
} dict;
type和privdata是为多态字典而设置,type保存了特定字典的计算函数,privdata为可选的私有数据。ht是一个size=2的数组,一般使用ht[0],进行rehash时会使用ht[1],当rehash进行时trehashidex为其进度,没有在rehash时为-1.
- 哈希函数,索引计算
hash = dict -> type -> hashFunction(key)
index = hash & dict -> ht[0].sizemask
MurmurHash2作为hash算法。 - 冲突解决,使用链地址法。
- rehash
- ht[1] 分配空间
扩容 ht[1]的大小为大于等于ht[0]的used * 2的 第一个2的n次幂。收缩 ht[1]的大小为大于等于ht[0]的used 的第一个2的n次幂。 - 将ht[0]上所有的键值保存到ht[1]上,
- 释放ht[0],然后将ht[1]设置为ht[0]
- 当没有在进行bgsave或者aoprewrite的时候,负载因子大于等于1或者在进行bgsave或者bgrewriteaof时负载因子大于等于5,自动进行扩容,当负载因子小于0.1时,自动缩容.
- 负载因子 = ht[0].used / ht[0].size
- 渐进式rehash,为了避免服务停止,使用trehashidex记录rehash进度。在rehash期间,每次对字典进行操作时,除了正常的操作,还会将trehashidx上的键值复制到ht[1]上,操作完成后trehashidx +1
- rehash期间,ht[0]和ht[1]同时提供服务,查询会先查询ht[0],然后再查询ht[1],新增操作一律在ht[1]上执行。
- ht[1] 分配空间
跳跃表
跳跃表是一种有序的数据结构,他通过在一个节点中维护多个指向其他节点的指针,来快速访问节点。是有序集合的一种实现。
复杂度:平均O(logn)最坏 O(n)
跳跃表节点
typedef struct zskiplistNode {
// 层
struct zskiplistLevel {
zskiplistNode *forward;// 前进指针
unsigned int span; // 跨度
} zskiplistLevel[];
// 后退指针
struct zskiplistNode *backward;
// 分值
double score;
// 成员
robj *obj;
}zskiplistNode;
- 每次创建节点时,根据幂次定律随机生成一个1-32的数作为层数组的大小。层的跨度表示两个节点之间的距离
- score & obj score可以重复,按照score排序,obj不能重复
整数集合
集合键的实现之一,只包含整数,且元素不多时,使用该数据结构。
升级
压缩列表
实现列表和hash键