zoukankan      html  css  js  c++  java
  • Redis 低级数据结构:一、介绍

    Redis低级数据结构##

    简单动态字符串###

    一般的可变字符串用的都是这个,好处就是返回长度和剩余的长度都是O(1)的复杂度,另外自动提供扩容,不会溢出,另外因为free扩容后会自动预分配一些,阈值在不同情况下是不同的,最大多分配1MB的空间,因此减少了重分配次数,另外减少字符串长度时,除了清掉buf[]中的值,还会修改free的值,并不会主动释放内存空间(提供api释放,不会造成浪费)以便使用时不需要再次分配。

    struct sdshdr {
        //当前长度
        int len;
        //剩余可用长度
        int free;
        char buf[];
    }
    

    链表###

    listNode是一个双向链表对象,list是一个链表的工具类,一般用list。

     typedef struct listNode {
        struct listNode *prev;
        struct listNode *next;
        void *value;
     }
    
     typedef struct list{
        listNode *head;
        listNode *tail;
        unsigned long len;
        void *(*dup)(void *ptr);
        void (*free)(void *ptr);
        int (*match)(void *ptr,void *key);
     }
    

    字典###

    和java的hashmap结构差不多, size代表键值对的数量,sizemask是size-1,用来计算索引的,就是java的hash&length,这里的是hash=dict->type->hashFunction(key) 然后再计算hash&sizemask。然后每个dictEntry是一个单向链表,这里不会像hashmap一样,超过8个就转为rbtree。
    然后是扩容过程,扩容标志是dict.trehashidx,-1代表正常,!-1代表扩容中。ht[0]代表正常使用的字典结构,当扩容时会渐进式hash把ht[0]里的值转移到ht[1]里面,此时所有对hash表的操作都在ht[1]里面,扩容完毕后,再把ht[0]释放,将ht[1]设为ht[0],最后重新建一个新的ht[1]

    typedef struct dictEntry{
        void *key;
        union{
            void *val;
            uint64_t u64;
            int64_t s64;
        } v
        struct dictEntry *next;
    }
    
    typedef struct dictht{
        dictEntry **table;
        unsigned long size;
        unsigned long sizemask;
        unsigned long used;
    }
    
    typedef struct dict{
        dictType *type;
        void *privdata;
        dictht ht[2];
        int trehashidx;
    }
    
    

    跳跃表###

    一种查询效率为O(logn)的数据结构,和平衡二叉树的效率类似。遍历时最慢的就是通过tail->backward 来遍历所有节点.快的就是通过跳跃表的层级来遍历,zskiplistNode.level[]代表了每个节点的层级,索引从小到大代表0级,1级...n级,zskiplistLevel.span代表的意思是两个节点的跨度(配合score来判断该怎么跳),zskiplist.level代表了跳跃表的最大层数。这样如果链表长度很多的时候,就通过level开辟了新的通道来查找元素。

    typedef struct zskiplistNode{
        struct zskiplistNode *backward;
        double score;
        robj *obj;
        struct zskiplistLevel{
            struct zskiplistNode *forward;
            unsigned int span;
        } level[]
    }
    
    typedef struct zskiplist{
        struct zskiplistNode *header , *tail;
        unsigned long length;
        int level;
    }
    
    

    整数集合###

    contents数组的值是有序的,而且不重复。encoding有三种,INTSET_ENC_INT16、INTSET_ENC_INT32、INTSET_ENC_INT64 ,就是contents里面数的范围不一样。int16是-32768
    32767,int32是-2147483648 ~ 2147483647
    另外就是升级的问题,整数集合里面的数据,会自动根据插入的值来升级,比如原来是int16的,然后插入一个int32,那么会遍历所有数据,将int16升为int32,没有降级过程。这样好处是在遍历的时候不会出现类型转换异常,而且也尽量节省了内存。
    typedef struct intset{
        uint32_t encoding;
        uint32_t length;
        int8_t contents[];
    }
    

    压缩列表###

    由一系列特殊编码的连续内存块组成的顺序型数据结构,一个压缩列表可以包含任意多个节点,每个节点可以保存一个字节数组或整数值。

    属性 类型 长度 用途
    zlbytes uint32_t 4字节 记录整个压缩列表占用的内存字节数:内存重分配或计算zlend位置时使用
    zltail uint32_t 4字节 记录尾节点地址和起始地址的内存偏移量,快速定位用的
    zllen uint16_t 2字节 记录了节点数量,小于65535时 代表了节点数量, 等于65535时需要遍历才能知道具体数量
    entryX 节点 不定 每个节点,长度由保存的内容决定
    zlend uint8_t 1字节 特殊值 0xFF ,标记压缩列表的末端


    图片参考的这篇博客!

    节点的结构

    属性 长度 用途
    previous_entry_length 1或5字节 压缩列表中前一个列表的长度,前面长度小于254,则为1字节,否则为5字节,通过zltail和这个就能从后往前遍历所有节点了
    encoding 1字节、2字节或5字节 记录content中保存的值的属性和类型,00、01、10是字节数组编码,11开头是整数编码
    content 由encoding中保存 保存节点的值

    连锁更新
    previous_entry_length 一堆1字节的节点堆一块儿了,然后前面插入了一个266字节的节点,那么就会导致新插入节点后面的所有1字节的节点依次更新,删除同理。就是连锁更新。最坏时间复杂度为O(N^2) 但是形成条件苛刻,知道有这回事就行,不用太在意。

  • 相关阅读:
    Android不规则瀑布流照片墙的实现+LruCache算法
    嵌入式OS入门笔记-以RTX为案例:六.RTX的任务调度
    Oracle backgroup processes
    Android中数据库的操作流程详解
    Dreamweaver PHP代码护眼配色方案
    Twitter 新一代流处理利器——Heron 论文笔记之Heron架构
    Docker简单介绍
    C#下使用GDAL
    Android:实现仿 美团/淘宝 多级分类菜单效果
    KVC在定义Model类中的妙用
  • 原文地址:https://www.cnblogs.com/june777/p/11910327.html
Copyright © 2011-2022 走看看