Redis本身是一个基于C语言开发的内存数据库,它包含String、Set、List、Hash、Set、ZSet等数据类型。那么它底层的数据结构是什么样的呢?简单而言,redis的数据结构包含简单动态字符串、链表、字典、跳跃表、整数集合、压缩列表等。
Redis没有直接用C语言的字符串,而是自己构建了一种名为【简单动态字符串】(Simple Dynamic String, SDS)的抽象类型,并将SDS作为Redis的默认字符串类型表示。
struct sdshdr{
//记录buf数组中已使用字节的数量
//等于 SDS 保存字符串的长度
int len;
//记录 buf 数组中未使用字节的数量
int free;
//字节数组,用于保存字符串
char buf[];
}
-
常量复杂度获取字符串长度,直接读取len属性,而不需要通过遍历计数
-
杜绝缓冲区溢出。C语言在strcat函数进行拼接时,如果没有分配足够长度的内存空间,就会造成缓冲区溢出。而对于SDS数据类型,在进行字符修改时,会首先根据len属性检查内存空间是否满足需求,如果不满足,会进行空间扩展。
-
减少修改字符串的内存重新分配次数。C语言不记录字符串的长度,如果要修改字符串,必须要重新分配内存。SDS通过len和free属性的存在,对于修改字符串SDS实现了空间预分配和惰性空间释放2种策略:
(1)空间预分配:对字符串进行空间扩展时,扩展的内存比实际需要的多,可以减少分配次数
(2)惰性空间释放,对字符串进行缩短操作时,程序不立即使用内存重新分配来回收缩短后多余的字节,而是使用free属性将这些字节的数量记下来,等待后续使用。
-
二进制安全。SDS的API都是以二进制的方式来处理buf中的元素,并且SDS不是以空字符串来判断是否结束,而是以len属性表示的长度来判断字符串是否结束。
-
typedef struct listNode{
//前置节点
struct listNode *prev;
//后置节点
struct listNode *next;
//节点的值
void *value;
}listNode
typedef struct list{
//表头节点
listNode *head;
//表尾节点
listNode *tail;
//链表所包含的节点数量
unsigned long len;
//节点值复制函数
void (*free) (void *ptr);
//节点值释放函数
void (*free) (void *ptr);
//节点值对比函数
int (*match) (void *ptr,void *key);
}list;
-
-
无环:表头节点的prev指针和表尾的next指针都指向NULL,对链表的访问都是以NULL结束
-
-
多态:链表节点使用void * 指针来保存节点值,可以保存不同类型的值
typedef struct dictht{
//哈希表数组
dictEntry **table;
//哈希表大小
unsigned long size;
//哈希表大小掩码,用于计算索引值
//总是等于 size-1
unsigned long sizemask;
//该哈希表已有节点的数量
unsigned long used;
}dictht
哈希表是由数组table组成,table中的每个元素都是指向entry结构
typedef struct dictEntry{
//键
void *key;
//值
union{
void *val;
uint64_tu64;
int64_ts64;
}v;
//指向下一个哈希表节点,形成链表
struct dictEntry *next;
}dictEntry
typedef struct zskiplistNode {
//层
struct zskiplistLevel{
//前进指针
struct zskiplistNode *forward;
//跨度
unsigned int span;
}level[];
//后退指针
struct zskiplistNode *backward;
//分值
double score;
//成员对象
robj *obj;
} zskiplistNode
typedef struct zskiplist{
//表头节点和表尾节点
structz skiplistNode *header, *tail;
//表中节点的数量
unsigned long length;
//表中层数最大的节点的层数
int level;
}zskiplist;
整数集合是Redis用于保存整数值的集合抽象数据类型。
压缩列表是Redis为节省内存而开发的顺序型数据结构,通常作为列表键和哈希键的底层实现之一。
大多数情况下,Redis使用简单字符串SDS作为字符串的表示,相对于C语言字符串,SDS具有常数复杂度获取字符串长度,杜绝了缓存区的溢出,减少了修改字符串长度时所需的内存重分配次数,以及二进制安全能存储各种类型的文件,并且还兼容部分C函数。
通过为链表设置不同类型的特定函数,Redis链表可以保存各种不同类型的值,除了用作列表键,还在发布与订阅、慢查询、监视器等方面发挥作用(后面会介绍)。
Redis的字典底层使用哈希表实现,每个字典通常有两个哈希表,一个平时使用,另一个用于rehash时使用,使用链地址法解决哈希冲突。
跳跃表通常是有序集合的底层实现之一,表中的节点按照分值大小进行排序。
整数集合是集合键的底层实现之一,底层由数组构成,升级特性能尽可能的节省内存。
压缩列表是Redis为节省内存而开发的顺序型数据结构,通常作为列表键和哈希键的底层实现之一。
参考链接:https://www.cnblogs.com/ysocean/p/9080942.html