您的位置:首页 > 理论基础 > 数据结构算法

Redis底层的几种数据结构

2019-04-01 20:33 525 查看

在redis中有五种对象,分别是字符串对象,列表对象,哈希对象,集合对象,有序集合对象。五种对象的具体实现对应六种不同的存储结构,redis支持在不同的数据规模下为每种对象选取不同的存储结构,以达到最好的适应性。下面先介绍六种底层存储结构。

 

SDS简单动态字符串

redis用来存储字符串的数据结构,可以看作是一个vector维护的字符数组。和C字符串的区别在于存了字符长度,保证不会缓冲溢出(类似vector的resize操作),针对可能的频繁数据修改进行空间预分配,和stl中的string一样是二进制安全的。

预分配策略具体分为两个部分。如果SDS进行修改之后,长度小于1mb,那么将预留一倍的空间,类似vector的分配策略。如果修改后长度大于1mb,那么将预留1mb的空间,减少内存占用。

二进制安全是指可以存储‘\0’字符,因为在使用中SDS不以字符串中的‘\0’作为结尾。

List链表

redis中的链表是无环双向链表,STL中是环形双向链表。包含两个结构体,listNode和list,其中list中存着链表的头结点,尾节点,链表长度。listNode中保存前后指针和对象指针。redis中的链表是多态的,它使用void*指针保存节点值。

hashmap字典

redis中字典用哈希表实现,哈希表的数据结构如下

[code]typedef struct dictht
{
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
}dictht;

分别是哈希表数组指针,哈希表大小,哈希表大小掩码(用于计算索引值,大小总是size-1),哈希表已有节点数量。

使用开链法处理冲突,哈希表节点结构如下

[code]typedef struct dictEntry
{
void*key;
union{
void*val;
uint64_t u64;
int64_t s64;
}v;
struct dictEntry *next;
}dictEntry;

同样使用指针完成多态。

每个字典中有一个包含两个哈希表指针的数组,其中一个用于存储,另一个在执行rehash的时候作为临时哈希表使用。

哈希表采用渐进式rehash,当原来表的大小过大或者过小的时候(这主要根据哈希表的负载度来判定),它会给没有使用的那个哈希表指针申请一个合适大小的表,并且在这之后所有对哈希表的插入都插向新表,把原表的数据逐步转移到新表中。具体一点就是以rehashidx作为标志,依次把原表中存储在rehashidx上的数据转移,转移完了把rehashidx加1,直到原表为空。这期间所有的查询操作都要在两个表上进行。

skiplist跳表

跳表是一种有序数据结构,他通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问的目的。跳表的查找平均复杂度为logn,最差为n,还可以通过顺序性操作来批量处理节点。

intset整数集合

其实整数集合就是顺序数组,插入删除查找效率都很低下,只支持一个一个迭代过去,复杂度为On。redis把它纳入底层纯属结构的原因在于,虽然哈希表,跳表各方面的复杂度都比顺序结构高得多,但是当数据量很小的时候,他们的优势并不能很好的体现出来,反而会占用很多额外的空间用于指针的存储。所以在数据量小于某个阈值的情况下,redis会选择用顺序结构进行存储,当超过了这个阈值的时候,redis会自动切换存储结构。

ziplist压缩列表

压缩列表和整数集合一样,都是redis为了在小数据量情况下节约内存所做出的选择。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: