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) 但是形成条件苛刻,知道有这回事就行,不用太在意。

  • 相关阅读:
    UVA12125 March of the Penguins (最大流+拆点)
    UVA 1317 Concert Hall Scheduling(最小费用最大流)
    UVA10249 The Grand Dinner(最大流)
    UVA1349 Optimal Bus Route Design(KM最佳完美匹配)
    UVA1212 Duopoly(最大流最小割)
    UVA1395 Slim Span(kruskal)
    UVA1045 The Great Wall Game(二分图最佳匹配)
    UVA12168 Cat vs. Dog( 二分图最大独立集)
    hdu3488Tour(KM最佳完美匹配)
    UVA1345 Jamie's Contact Groups(最大流+二分)
  • 原文地址:https://www.cnblogs.com/june777/p/11910327.html
Copyright © 2011-2022 走看看