redis数据结构和对象二

2022-12-26,,

    跳跃表(skiplist)

    跳跃表是一种有序数据结构。跳跃表支持平均O(logN),最坏O(N)复杂度的节点查找,大部分情况下,跳跃表的效率可以和平衡树相媲美,并且因为跳跃表的实现比平衡树简单,所有不少程序都用跳跃表代替平衡树。Redis使用跳跃表作为有序集合的底层实现,另一个是在集群节点中用作内部数据结构。

1.1 跳跃表的实现

typedef struct zskiplist {

struct zskiplistNode *header, *tail;

unsigned long length;

int level;

} zskiplist;

header:指向跳跃表的表头节点。
tail:指向跳跃表的表尾节点。
level:记录目前跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算在内)
lenght:记录跳跃表的长度,即跳跃表目前包含节点的数量(表头节点的层数不计算在内)

typedef struct zskiplistNode {

robj *obj;

double score;

struct zskiplistNode *backward;

struct zskiplistLevel {

struct zskiplistNode *forward;

unsigned int span;

} level[];

} zskiplistNode;

层(level):节点中用L1, L2, L3等字样标记节点的各个层,L1代表第一层,L2代表第二层,依次类推。每个层都带有两个属性:前进指针和跨度。
前进(level[i].forward)属性:用于从表头向表尾方向的访问节点。遍历跳跃表中所有节点的路径。
跨度(level[i].span)属性:用于记录两个节点之间的距离。两个节点直接的跨度越大,他们相距的就越远。指向NULL的所有指针的跨度都为0,因为他们没有连向任何节点。
后退(backward)指针:节点中用BW字样标记节点的后退指针,他指向位于当前节点的前一个节点。后退指针在程序从表尾向表头遍历使用。
分值(score):各个节点中的1.0,2.0和3.0是节点所保存的分值。在跳跃表中节点按各自所保存的分值从小到大排列。
成员对象(obj): 各个节点中o1、o2、o3是节点所保存的成员对象。

    整数集合(intset)

整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合元素数量不多时,Redis就会使用整数集合作为集合键的底层实现。

typedef struct intset {

uint32_t encoding;

uint32_t length;

int8_t contents[];

} intset;

contents数组时整数集合的底层实现:各个元素在数组中按值大小从小到大有序地排列,并且数组中不包含任何重复项。
length属性记录了整数集合包含的元素数量,即contents数组数组的长度。

虽然intset结构将contents属性声明为int8_t类型的数组,但实际上contents数组并不保存任何int8_t类型的值,contents数组的真正类型取决于encoding属性值。
如果encoding属性的值为INTSET_ENC_INT16,那么contents就是一个int16_t类型的数组。数组长度sizeof(int16_t)*length
如果encoding属性的值为INTSET_ENC_INT32,那么contents就是一个int32_t类型的数组。数组长度sizeof(int32_t)*length
如果encoding属性的值为INTSET_ENC_INT64,那么contents就是一个int64_t类型的数组。数组长度sizeof(int64_t)*length

整数集合升级,不支持降级。

每当将一个元素添加到整数集合里面,并且新元素的类型比整数集合现有所有元素的类型都要长时,整数集合需要先进行升级,然后才能添加到整数集合里面。升级分三步进行:

1)根据新元素的类型,扩展整数集合底层数组的空间大小,并为新元素分配空间。

2)将底层数组现有的所有元素都转换成与新元素相同的元素类型,并将类型转换后的元素放置到正确的位置。

3)将新元素添加到底层数组里面。

升级的好处

1)提升灵活性。因为语言时静态类型语言,不能将两种不同类型的值放在同一个数据结构里面。因为整数集合可以自动升级底层数组来适应新元素。

2)节约内存。 要让一个数组同时可以保存int16_t,int32_t, int64_t三种类型通常做法直接使用int64_t类型作为数组,这样出现浪费内存。

升级例子:将一个类型int32_t的数值65535添加到int16_t类型集合里面:

    压缩列表(ziplist)

    压缩列表时列表键和哈希键的底层实现只一,当一个列表只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表。

3.1 压缩列表的构成

压缩列表时Redis为了节约内存而开发,是由一系列特殊编码的连续内存块组成的顺序型的数据结构。一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值。

3.1 压缩列表节点的构成

每个压缩列表节点可以保存一个字节数组或者一个整数值。其中,字节数组可以是以下三种长度中的一种:

长度小于等于63(2^6-1)字节的字节数组;

长度小于等于16383(2^14-1)字节的字节数组

长度小于等于4294967295(2^32-1)字节的字节数组

整数值可以是以下6种长度中的一种:

4位长,介于0至12之间的无符号整数

1字节长的有符号整数

3字节长的有符号整数

int16_t类型整数

int32_t类型整数

int64_t类型整数

节点的 previous_entry_length属性以字节为单位,记录了压缩列表中前一个节点的长度。

如果前一节点的长度小于254字节,那么 previous_entry_length属性的长度为1字节,

如果前一节点的长度大于等于254字节,那么 previous_entry_length属性的长度为5字节:其中属性的第一字节会被设置为0xFE(十进制值254),而之后的四个字节则用于保存前一节点的长度。

节点的encoding属性记录了节点的content属性所保存数据的类型以及长度。

一字节、两字节或者五字节长,值的最高位为00、01或者10的是字节数组编码:这种编码表示节点的 content属性保存着字节数组,数组的长度由编码除去最高两位之后的其他位记录。

一字节长,值的最高位以11开头的是整数编码:这种编码表示节点的content属性保存着整数值,整数值的类型和长度由编码除去最高两位之后的其他位记录。

节点的content属性负责保存节点的值,节点值可以是一个字节数组或者整数,值的类型和长度由节点的encoding属性决定。

节点的content属性负责保存节点的值,节点值可以是一个字节数组或者整数,值的类型和长度由节点的encoding属性决定

redis数据结构和对象二的相关教程结束。

《redis数据结构和对象二.doc》

下载本文的Word格式文档,以方便收藏与打印。