redis作为日常开发中常用的组件,在深入了解之前,我们先简单的来了解一下其对象以及对象的底层数据结构。
数据结构
在了解redis常用的对象之前,我们首先了解一下器底层实现的数据结构。
简单动态字符串
在redis数据库中,包含字符串的键值对在底层都是由SDS来实现。
struct sdshdr{
int len;//为SDS所保存的字符串的长度,也就是以使用的字节数
int free;//未使用的字节数
cahr buf[];//用于保存字符串
}
并且SDS遵循C字符串以空字符串结尾的惯例,保存空字符串的1字节不计算在len属性中。
链表
对于链表,不在进行过多的叙述,网上能找到大量的资料。在redis中的链表采用的是双向链表,它共有以下几个属性:
- head:表头zhizhen
- tail:表尾指针
- len:链表所包含的节点数量
包含以下几个方法:
- dup:用于复制链表节点保存的值
- free:用于释放节点保存的值
- match:用于判断链表节点保存的值是否与另一个输入值相等
字典(映射)
类似java的map,是一个key-value键值对,它底层就是使用哈希表实现的,一个哈希表中包含多个哈希节点,哈希节点保存了键值对。我们重点看下字典中对哈希表的使用。它拥有一个哈希表的数组ht属性,大小为2。一般情况之下,我们只使用ht[0],h[1]只在h[0]进行rehash的时候使用。
rehash的操作发生在哈希表的扩容或者收缩的时候,首先为字典的ht[1]哈希表分配空间,如果是扩展,ht[1]分配的空间是一个大于等于ht[0]*used的2的n次幂(比如ht[0]已使用空间是31,那么ht[1]的空间为2*31<64,取空间大小为64),如果是缩容,那么大小为大于等于ht[0]已使用空间的2的n次幂的大小。然后将ht[0]的键值对迁移至ht[1],删除ht[0],并将ht[1]设置为ht[0],最后将ht[1]新创建一个空白的哈希表。
跳跃表
首先我们先简单介绍一下跳跃表,首先是一个排序好的队列,我们要查找其中的一个元素,必须从头到尾循环遍历找到该元素。
但是我们可以开发一条捷径去访问我们需要索引的元素,
在上图中,我们我们假设要查找46,首先在L3层查找,由于链表是有序的,46一定在55左边,到下一层继续查找,同理确定其位置在37到55之间,那么在往下一层查找,我们只需要一层查找便可以找到46。
参考博客(https://blog.csdn.net/qpzkobe/article/details/80056807)
在redis中使用zskiplist结构来持有跳跃节点,包含一下属性
- head:表头节点
- tail:表尾节点
- length:节点的数量
- level:最大节点的层数
整数集合
用于保存整数的集合,可以保存的类型是int16,int32,int64。redis中的实现包含以下几个属性:
- encoding:编码方式
- length:包含的元素个数
- contents:保存的元素数组
注意:每当我们插入一个新的元素,如果新元素的长度大于现有元素的长度,那么需要升级。比如原来存的是int16,如果新插入的元素是int32,那么之前的元素需要都升级为int32。扩展操作分为以下几步:
- 根据新元素的类型进行空间的扩展,并分配空间。
- 将原来的元素转换为新的类型,并按照原来的顺序放到新的数组中
- 添加新的元素
压缩列表
当列表中包含少量元素,并且元素要么是小整数值,要么是短的字符串,那么采用压缩列表来作为键值的底层实现。盗redis设计与实现的图:
- zlbytes:记录压缩列表占用的内存字节数
- zltail:表示尾节点距离起始节点有多少个字节
- zllen:包含的节点的数量
- entry:节点
- zlend:标记压缩列表的尾端
其中节点的属性如下:
- previous_entry_length:前一个节点的长度
- encoding:content属性中值的属性
- content:值