【Redis】Redis数据类型底层结构
参考:《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;
- 上面的整个哈希表,出现了哈希冲突,使用链地址法解决哈希冲突;