• redis数据结构底层剖析学习笔记2


    四.跳跃表

    跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。

    redis使用跳跃表作为有序集合的底层实现之一,如果一个有序集合包含的元素数量比较多,又或者有序集合中元素的成员是比较长的字符串时,redis就会使用跳跃表来作为有序集合键的底层实现。redis只在两个地方用到跳跃表,一个是实现有序集合键,另一个是在集群节点中用作内部数据结构。

    4.1 跳跃表的实现

    跳跃表由redis.h/zskiplistNode和redis.h/zskiplist两个结构定义,其中zskiplistNode结构用于表示跳跃表节点,而zskiplist结构则用于保存跳跃表节点的相关信息。

    typedef struct zskiplistNode{
    	//层
    	struct zskiplistLevel{
    		//前进指针
    		struct zskiplistNode *forward;
    		//跨度
    		unsigned int span;
    	} level[];
    	//后退指针
    	struct zskiplistNode *backward;
    	//分值
    	double score;
    	//成员对象
    	robj *obj;
    }zskiplistNode;
    

    (1).层

    跳跃表节点的level数组可以包含多个元素,每个元素都包含一个指向其他节点的指针,程序可以通过这些层加快访问其他节点的速度,一般来说,层的数量越多,访问其他节点的速度就越快。

    每次创建一个新跳跃表节点的时候,程序根据幂次定律随机生成一个介于1和32之间的值作为level数组的大小,这个大小就是层的“高度”。

    (2).前进指针

    (3).跨度

    层的跨度用于记录两个节点之间的距离:

    >两个节点之间的跨度越大,那么距离越远;

    >指向null的所有前进指针的跨度是0。

    跨度是用来计算排位(rank)的。

    (4).后退指针

    和可以一次跳过多个节点的前进指针不同,因为每个节点只有一个后退指针,所以每次只能后退到前一个节点。

    (5)分值和成员

    节点的分值是一个double类型的浮点数,跳跃表的所有节点都是按分值从小倒大排序。

    节点的成员对象是一个指针,它指向一个字符串对象,而字符串对象则保存着一个SDS。

    在同一张跳跃表中,各个节点保存的成员必须是唯一的,但是多个节点保存的分值却可以是相同的:分值相同的节点将按照成员对象在字典序中的大小进行排序,小的排前面。

    typedef struct zskiplist{
    	//层
    	struct zskiplistNode *header,*tail;
    	//表中节点的数量
    	unsigned long length;
    	//表中层数最大的节点的层数
    	int level;
    }zskiplist;
    

    header和tail指针分别指向跳跃表的表头和表尾节点,通过这两个指针,程序定位表头节点和表尾节点的复杂度是O(1)。通过length属性记录节点的数量。

    六.整数集合

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

    typedef struct intset{
    //编码方式
    uint32_t encoding;
    //集合包含的元素数量
    uint32_t length;
    //保存元素的数组
    int8_t contents[];
    }intset;

    contents数组是整数集合的底层实现:整数集合的每个元素都是contents的一个数据项,每个项在数组中按值的大小从小到大排列,并且数组中不包含任何重复项。

    另外还有一点就是 假设一个新元素add到整数集合中,并且新元素的encoding类型比整数集合现有所有元素的类型都要长时,整数集合需要先进行升级,然后才能讲新元素添加到整数集合中。一旦对数组进行了升级,无法降级。

    七.压缩列表

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

    redis>rpush lst 1 3 5 10086 "hello" "world"
    (integer) 6
    redis>OBJECT ENCODING lst
    "ziplist"

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

    redis>hmset profile "name" "Jack" "age" 28 "job" "teacher"
    OK
    
    redis>OBJECT ENCODING profile
    "ziplist"

    7.1 压缩列表的构成

    压缩列表是redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构。

    一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值。

    7.2 压缩列表节点的构成

     每个压缩列表节点都可以保存一个字节数组或者一个整数值。

    (1)节点的previous_entry_length是以字节为单位,记录着前一节点的长度。通过这个属性以及当前节点的起始值,可以计算出上一节点的起始值。另外当前一节点的长度小于254字节,那么previous_entry_length的长度是1字节,如果前一节点的长度大于等于254字节,那么previous_entry_length的长度是5字节

    (2)encoding记录了节点content属性所保存数据的长度和类型 :

    >值的最高位是00,01,10的话,content保存的是字节数组;

    >值的最高位是11的话,content保存的是整数。

    7.3 连锁更新

    当对压缩列表进行新增或者删除时,可能会发生连锁更新(因为当前一节点变成大于254字节的时候,当前节点previous_entry_length会由1字节变成5字节,假设恰好当前节点此时也变成大于254字节,那么当前节点的下一个节点的previous_entry_length也会变化....依次进行,产生了连锁更新)

  • 相关阅读:
    静态邻接表dijkstra
    最短路径系列【最短路径、哈密顿路等】
    python 给多人发送邮件,且将结果添加为附件
    Excel调换数据位置
    try ... except...,好处是执行失败后,仍然可以继续运行
    制作表头,2种方式
    工资表变工资条,2种方式
    C言语教程第一章: C言语概论 (4)
    从红旗5.0说起——看Linux的内存解决
    红旗Linux桌面4.1文本安装历程图解(二)
  • 原文地址:https://www.cnblogs.com/juin1058/p/11573633.html
Copyright © 2020-2023  润新知