Redis基本数据类型及其内部结构总结

redis支持多种数据类型,string,list,hash,set,zset,这个可能很多人都已经了如指掌了,但是redis中这些基本的数据类型都是由什么数据机构进行实现的呢,这其中的原理又是怎么样的呢?这篇文章主要来针对redis中每种数据类型的具体实现进行详细介绍。

基本的数据结构

首先,需要整体上了解一下redis中用到的一些基本数据结构的含义和概念。
1、字符串SDS:简单动态字符串,redis根据C语言字符串自身进行修改所支持的字符串类型。

struct sdsstr{
//记录buf数组中已使用字节的数量
	int len;
	//记录buf数组中未使用字节数量
	int free;
	//字节数组
	char buf[];
}

2、双端链表:具有前驱节点和后继节点的一个无环双向链表
3、字典:内部实现是hashtable,一个字典中包含了两个hashtable,组成了一个数组dt[2],两个哈希表是为了方便扩容,类似于java中的HashMap。而每个hashtable又是由数组和链表组成的。
4、压缩链表:一个列表中如果只包含整数或者较短的字符串时,这个数据机构被称为压缩链表。它在内存中会分配一段连续的内存空间,所以他的出现时为了节省内存而设计的。
5、整数集合:在redis中会把一个键值只包含整数值的数据机构分配在一块连续的空间内,这种数据机构叫整数集合,它的出现也是为了节约内存设计。
6、跳跃表:在有序节点中,每个节点维持多个指向其他节点的指针,以达到快速访问的目的的一种数据机构。

redis中的对象属性

typedef struct redisObject{
	//记录对象的类型包括:字符串对象,列表对象,哈希对象,集合对象或者有序集合对象其中的一种
	//值分别为:REDIS_STRING,REDIS_LIST,REDIS_HASH,REDIS_SET,REDIS_ZSET
	unsigned type:4;
	//编码,即此对象使用了什么数据结构,这里就涉及到Redis中用到的很多种数据结构。
	unsigned encoding;
	//指向底层实现数据结构的指针
	void *ptr;
	
}

其实今天这篇文章接下来的主要内容就是在介绍redis中各个对象的encoding和ptr。

字符串对象

redis中字符串对象的编码格式分为三种:int,raw和embstr,这三种编码的使用情况如下:
1、如果一个字符串对象保存的是整数值,并且这个整数值可以用long类型来表示,那么字符串对象编码设置为int。
2、如果字符串对象保存的是一个字符串值,并且这个字符串值大于39字节,那么将使用sds来保存这个值,并将编码设置为raw。
3、如果这个字符串值小于39字节,则编码为embstr。

raw和embstr编码的区别:
embstr编码是专门用于保存短字符串的一种优化编码方式,这种编码和raw编码一样,都是用redisObject结构和sdsstr结构来表示字符串对象,单raw编码会调用两次内存分配函数来分别创建redisObject和sdsstr结构(redis对象中的ptr指向sdsstr),而embstr编码则通过调用一次内存分配函数来分配一块连续的空间,空间依次包含redisObject和sdsstr

embstr编码的对象是只读的,当执行任何修改命令时,程序会先将embstr改成raw,然后再执行修改命令。

列表对象

列表对象的编码可以是ziplist或linklist。
ziplist使用条件:
1、列表对象保存的所有字符串元素长度都小于64字节;
2、列表对象保存的元素数量小于512;
使用了ziplist的列表对象如下图:
Redis基本数据类型及其内部结构总结
使用了linklist的列表对象如下图:
Redis基本数据类型及其内部结构总结

哈希对象

哈希对象编码为ziplist或者hashtable
ziplist编码的哈希对象每当有新的键值对要加入到哈希对象时,程序会将保存了键的压缩列表节点推入到压缩列表表尾,然后将保存了值得压缩列表节点推入到压缩列表的表尾。
使用ziplist条件:
1、哈希对象保存的所有简直对的键和值得字符串长度都小于64字节
2、哈希对象键值对数量小于512个
使用了ziplist的哈希对象如下图:
Redis基本数据类型及其内部结构总结
使用了hashtable的哈希对象如下图:
Redis基本数据类型及其内部结构总结

集合对象

集合对象的编码可以是intset或者hashtable
以hashtable作为集合的底层实现时,元素的值全部设置为null。
使用intset整数集合的条件:
1、集合对象所有元素都是整数值
2、元素数量不超过512个

以整数集合为编码的集合对象如下图:
Redis基本数据类型及其内部结构总结
以hashtable为编码的结合对象如下图:
Redis基本数据类型及其内部结构总结

有序集合对象

有序集合对象的编码方式可以为ziplist或者skiplist。
使用ziplist编码的有序集合对象每个元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,而第二个元素则保存元素的分值。压缩列表内的集合元素分值从小到大进行排序,分值较小的元素被放置在靠近表头的位置,而分值较大的元素则被放置在靠近表尾的位置。

使用ziplist编码的条件:
1、有序集合保存的元素数量小于128个
2、所有元素成员的长度小于64字节

使用ziplist编码的有序集合对象如图:
Redis基本数据类型及其内部结构总结
当有序集合在使用skiplist作为编码实现时,zset将同时使用dict和skiplist一起作为编码实现。原因是,当我们使用字典时,对查找性能友好O(1),而当使用跳跃表skiplist时又可以优化范围查询排序等操作。
使用skiplist时的有序集合如下简图:
Redis基本数据类型及其内部结构总结

总结

redis中5种数据类型对应redis中5种不同的redis对象。每种redis对象都有不同的数据编码实现,每种数据编码又对应不同的数据内存结构。

上一篇:关于redis,你需要了解的几点!


下一篇:Redis底层数据结构之list