参考:《Redis设计与实现》
RedisObject
Redis底层的所有数据结构:都是基于对象的;RedisObject
- 类型;
- 编码;
- 指向实际数据的指针;
typedef struct redisObject{
// 类型
unsigned type:4;
// 编码
unsigned encoding:4;
// 指向底层实现数据结构的指针
void *ptr;
}
-
type:记录是什么类型(String,List,Hah表,set,Zset)
-
encoding:对象使用的编码;
-
*ptr:数据指针(比如String,ptr就指向了一个SDS动态字符串)
一个String类型数据如下图:
String
Redis的字符串底层使用SDS(动态字符串),而不是C语言的字符串;
// SDS的定义
strcut sdshdr{
// 记录字符串长度
int len;
// 记录未使用的buf;
int free;
// 字符串数组,存放实际的字符串
char buf[];
}
为什么不使用C的字符串?
C字符串 | SDS |
---|---|
获取字符串长度复杂度:O(N) | 获取字符串长度复杂度:O(1) |
不安全,可能缓冲区溢出 | 安全,不会溢出,不会泄露 |
修改字符串,必须执行N次内存的重新分配 | 修改字符串,除非是全部改需要N次内存分配 |
跳跃表
是一种有序数据结构,通过每个节点中维持多个指向其他节点指针,达到快速访问;
多个节点指针:意味着跳跃表的层数;
- 层:每个节点的多个指针,是一个指针集合List;同层之前指针关联;
- 前进指针:每一层的从头指向后面的指针;
- 跨度:记录两个节点间的距离;
- 后退指针:每个节点,只有一个后退指针,也就是全部后退指针相当于一个单链表;
- 分值Score:跳跃表,按照Score大小排序;
- 成员:也就是此节点的value;
复杂度:增删改查O(logn),最坏O(n)
结构源码:
// zset结构
typedef struct zskiplist{
// 头节点,尾节点
structz skiplisNode *header , *tail;
// 节点数量
unsigned long length;
// 最大的节点层数;
int level;
}
// 跳跃表的结构
typedef struct skiplisNode{
// 层:是一个节点集合
struct zskiplistLevel{
// 前进指针
struct zskiplistNode *forward;
// 跨度
unsigned int span;
} level[];
// 后退指针
struct zskiplistNode *backward;
// 分值
double score;
// 成员对象
robj *obj;
}
Hash表
RedisObject中的ptr指针,指向的就是哈希对象:
typedef struct dict{
// 指向对哈希表操作的函数
dictType *type;
// 私有数据
void *privdata;
// ht[1]指向真正的哈希表结构,ht[2]用于备用扩容,指向正在扩容的哈希表
dictht ht[2];
// 是否在rehash:如果不在rehash,此值为-1;
int trehashidx;
}
哈希表:
typedef struct dictht{
// 哈希数组
dictEntry **table;
// 哈希表大小
unsigned long size;
// 记录尾部:size-1
unsigned long sizemask;
// 已使用的大小
unsigned long used;
}
哈希节点对象:
typedef struct dictEntry{
// key
void *key;
// value
union{
void *val;
unit64_tu64;
int64_ts64;
} v;
// next指针
struct dictEntry *next;
} dictEntry;
- 上面的整个哈希表,出现了哈希冲突,使用链地址法解决哈希冲突;