Redis的几种底层数据结构

Redis的几种底层数据结构

简单字符串

  • Redis的字符串是本身构建的一种名为简单动态字符串(SDS)的抽象类型。 和常规c语言字符串不一样segmentfault

  • SDS的定义以下:数组

struct sdshdr{
    int len; //记录buf数组中已使用字节的数量 既SDS所保存字符串长度
    int free; // 记录buf数组中未使用字节的数量
    char buf[]; //字节数组,用于保存字符串
};

如图所示 存储 “Redis ”这个字符串安全

  • SDS 能够常数获取字符串长度,里面的len记录的字符串的长度,因此能够直接获取。而c语言须要O(n)的复杂度
  • SDS能够杜绝缓冲区溢出。当API对SDS进行修改时,API会先检查SDS的空间是否知足修改需求若是不知足,会先扩展空间
  • 能够减小修改字符串时带来的内存分配次数。
    • 空间预分配。当SDS进行修改的时候,程序不只会为SDS分配修改所必须的空间,还会为SDS分配额外的未使用空间(free)
    • 惰性空间释放。当SDS的API须要缩短SDS保存的字符串时,使用free属性记录下来,并不会去回收这些内存
  • 二进制安全。全部SDS API 都是二进制安全,API都会以处理二进制的方式来处理SDS存放在buf数组里的数据。

链表

  • 链表提供了高效的节点重拍能力,以及顺序性的节点访问方式,而且能够经过增删节点来灵活地调整链表长度数据结构

  • 链表的结构:函数

    typedef struct listNode{
        // 前置节点
        struct listNode *prev;
        //后置节点
        struct listNode *next;
        //节点的值
        void *value;
    }listNode;
    
    typedef struct list{
        listNode *head; // 表头节点
        listNode *tail; // 表尾节点
        unsigned long len; // 链表所包含的节点数量
        void *(*dup) (void *ptr) // 节点复制函数
        void (*free)(void *ptr)  // 节点值释放函数
        int (*match)(void *ptr,void *key) //节点值对比函数
    }list;

字典

字典,又称为符号表,关联数组或映射,是一种用于保存键值对的抽象数据结构 ,在字典中,一个key和一个value进行关联,字典中的key是独一无二的ui

  • 字典的实现:编码

    1.哈希表:
      typedef struct dicht{
          dictEntry **table; // 哈希表数组
          unsigned long size;  // 哈希表大小
          unsigned long sizemask; // 哈希表大小掩码,用于计算索引值,老是等于size-1
            unsigned long used; // 该哈希表已有的节点数量
      }dicht;
    2.哈希表节点
      typedef struct dicEntry{
          void *key; //键
          union{    // 值
              void *val;
              uint64_t u64;
              int64_t s64;
          }v;
          struct dicEntry *next; // 指向下个哈希表节点,造成链表
      }dicEntry;
    3. 字典
      typedef struct dict{
          dictType *type; //类型特定函数
          void *privdata; //私有数据
          dictht ht[2];  // 哈希表
          int trehashidx;//rehash 索引,当rehash再也不进行时 值为-1
      }dict;
      typedef struct dictType{
            // 计算哈希值的函数
          unsigned int (*hashFunction)(const void *key);
            //复制键的函数
            void *(*keyDup)(void *privdata,const void *key);
            // 复制值的函数
            void *(*valDup)(void *privdata,const void *obj);
            // 对比键的函数
            int (*keyCompare)(void *privdata,const void *key1 ,const void *key2);
            //销毁键的函数
            void (*keyDestructor)(void *privdata,const void *key);
            //销毁值的函数
            void (*valDestructor)(void *privdata,const void *obj);
      }dictType;

    图示:指针

  • 解决键冲突问题。当有两个或以上数量的键被分配到哈希表数组的同一个因此上面时,咱们采用链地址法来解决键冲突code

图示:

  • rehash。随着操做的执行,哈希表保存的键值对会逐渐曾多或者减小,为了维护哈希表在一个合理范围内,进行rehash操做

    • 为字典的ht[1] 哈希表分配空间
    • 将保存在ht[0]的全部键值对从新计算哈希值和索引值,而后将键迁移到ht[1]
    • ht[0]包含的全部键值对都迁移到ht[1]以后(ht[0]变为空)释放ht[0],将ht[1]设置为ht[0],而且在ht[1]新建立一个空白哈希表为下一次rehash 作准备
  • 渐进式rehash。rehash的动做不是一次性集中式的完成 ,而是分屡次,渐进式的完成。

跳跃表

跳跃表是一种有序数据结构,经过每一个节点中维持多个指向其余节点的指针,从而到达快速到达访问节点的目的

整数集合

整数集合是集合键的底层实现之一,当一个集合只包含整数值元素,而且这个集合的元素数量很少的时候,Redis会使整数集做为集合键的底层实现

  • 集合的实现:

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

    图示:

  • 升级。 每当咱们将一个新元素添加到整数集合,而且新元素的类型比整数集合现有的元素类型要长时,咱们要进行升级

  • 升级的好处:提高灵活性,节约内存

  • 整数集合不支持降级操做,一旦升级,编码就会一直保持升级后的状态

压缩列表

压缩列表是列表键和哈希键的底层实现之一

  • 压缩列表的构成:

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

    如图所示:

  • previous_entry_length。节点的previous_entry_length属性以字节为单位,记录了压缩列表中前一点的长度,从表位向前遍历压缩列表时只用拥有一个只想某个节点的起始地址指针,经过这个指针以及这个节点的 previous_entry_length程序就能够向前一个节点回溯

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

  • content。节点的content负责保存节点的值

  • 连锁更新。若是当前列表的节点e1到eN的节点长度都小于254字节,若是咱们将一个长度大于等于254字节的新节点设置为压缩列表的表头时程序会不断地对压缩列表执行空间重分配操做直到eN位置,操做成为“连锁更新”

相关文章
相关标签/搜索