zoukankan      html  css  js  c++  java
  • redis源码——数据结构与对象

    redis数据库每个键值对都是由对象组成

    数据库键总是一个字符串对象;

    而数据库键的值则可以是字符串对象、列表对象、哈希对象、集合对象,有序集合对象。

    字符串

    redis没有直接使用c语言传统字符串,而是自己构建了一种名为简单动态字符串(SDS)的抽象类型。主要是为了解决''的问题。

    struct sdshdr {
      int len;         // buf 中已占用空间的长度 0
      int free;       // buf 中剩余可用空间的长度 5
      char buf[];   // 数据空间,最后一个字节保存空字符 'r''e''d''i''s''%0'
    };

    这样做的好处(长度、结束都由len判断,分配预留free)

    • 可以常数复杂度获取字符串长度。
    • 杜绝缓冲区溢出
    • 减少修改字符串时带来的内存重分配次数
    • 二进制安全
    • 兼容部分c字符串函数

    链表

    链表提供了高效和节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表的长度。

    链表在redis中的应用非常广泛,比如列表键的底层实现之一就是链表。当一个列表键包含了数量比较多的元素,又或者列表中包含的元素都是比较长的字符串时,redis就会使用链表为列表键的底层实现。

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

    redis的链表实现的特性:

    • 双端, 获取某个节点的前置节点和后置节点的复杂度都是O(1)
    • 无环:表头节点的prev指针和表尾节点的next指针都指向NULL,对链表的访问以NULL为终点。
    • 带表头表尾指针,获取俩复杂度为O(1)
    • 带链表长度计数器,获取节点数量的复杂度为O(1)
    • 多态:链表节点使用void*指针来保存节点值,并且可以通过list结构的dup、free、match三个属性为节点值设置类型特定函数,所以链表可以用于保存各种不同类型的值。

    字典

    reds的数据库就是使用字典来作为底层实现的,对数据库的增、删、查、改操作也是构建在对字典的操作之上的。

    除了用来表示数据库之外,字典还是哈希键的底层实现之一,当一个哈希键包含的键值对比较多,又或者键值对中的元素都比较长的字符串时,redis就会使用字典作为哈希键的底层实现。

    哈希:

    typedef struct dictEntry {
        void *key;        //
    
        union {
            void *val;
            uint64_t u64;
            int64_t s64;
        } v;                   //
    
        struct dictEntry *next;     // 指向下个哈希表节点,形成链表
    } dictEntry;
    typedef struct dictht {
        dictEntry **table;        // 哈希表数组
        unsigned long size;      // 哈希表大小
        unsigned long sizemask;     // 哈希表大小掩码,用于计算索引值,总是等于 size - 1
        unsigned long used;        // 该哈希表已有节点的数量
    } dictht;

    字典:

    typedef struct dict {
        dictType *type;         // 类型特定函数
        void *privdata;         // 私有数据
        dictht ht[2];             // 哈希表    
        int rehashidx;    // rehash 索引,当 rehash 不在进行时,值为 -1
        int iterators;     // 目前正在运行的安全迭代器的数量
    } dict;

    type属性和privdata属性是针对不同类型的键值对,为创建多态字典而设置的。

    ht属性是一个包含两个项的数组,数组中的每个项都是一个dictht哈希表,一般情况下,字典只使用ht[0]哈希表,ht[1]哈希表只会在对ht[0]哈希表进行rehash时使用。

    rehashidx它记录了rehash目前的进度,如果目前没有在进行rehash,那么它的值为-1。

    跳跃表(skiplist)

    https://www.cnblogs.com/losophy/p/10517293.html

    整数集合

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

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

    压缩列表(为了节约内存)

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

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

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

    unsigned char *ziplistNew(void) {
        unsigned int bytes = ZIPLIST_HEADER_SIZE+1; // ZIPLIST_HEADER_SIZE 是 ziplist 表头的大小, 1 字节是表末端 ZIP_END 的大小
        unsigned char *zl = zmalloc(bytes);    // 为表头和表末端分配空间
    
        ZIPLIST_BYTES(zl) = intrev32ifbe(bytes);    // 初始化表属性
        ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);
        ZIPLIST_LENGTH(zl) = 0;
    
        zl[bytes-1] = ZIP_END;    // 设置表末端
    
        return zl;
    }

    redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统。

    redis还在这对象系统中构建了一个引用计数技术实现的内存回收机制。

    typedef struct redisObject {
        unsigned type:4;    // 类型
        unsigned encoding:4;    // 编码
        unsigned lru:REDIS_LRU_BITS;    // 对象最后一次被访问的时间
        int refcount;    // 引用计数
        void *ptr;    // 指向实际值的指针
    } robj;

    参考:

    《redis设计与实现》

  • 相关阅读:
    关于jquery动态添加的新元素无法绑定事件那些事
    关于jquery获取json数据的格式问题
    beescms文章列表页函数学习
    beescms相关函数学习
    这是二零一四年十点整的广州
    POJ 1852 Ants 分析
    hiho_1114_扫雷
    hiho_1014_Trie_Tree
    排列组合
    用链表写的冒泡排序理解
  • 原文地址:https://www.cnblogs.com/losophy/p/9539648.html
Copyright © 2011-2022 走看看