zoukankan      html  css  js  c++  java
  • redis 字典

    字典:(符号表)

    字典就是一个存储kv的存储结构,类似与c++的map,redis数据库的底层就是使用字典实现的

    除了数据库,字典也是哈希键的底层实现

    字典使用哈希表实现,哈希表中存储的都是kv结构

    typedef struct dictht {
    
        // 哈希表数组
        dictEntry **table;
    
        // 哈希表大小
        unsigned long size;
    
        // 哈希表大小掩码,用于计算索引值
        // 总是等于 size - 1
        unsigned long sizemask;
    
        // 该哈希表已有节点的数量
        unsigned long used;
    
    } dictht;

    sizemask和哈希值一起决定了这儿节点应该放在哪里,我们每一个哈希表节点都有一个next属性,这个可以解决链表冲突的问题,使得多个键值一样的可以连在一起

    下面我们看一下哈希表节点的定义:

    typedef struct dictEntry {
    
        //
        void *key;
    
        //
        union {
            void *val;
            uint64_t u64;
            int64_t s64;
        } v;
    
        // 指向下个哈希表节点,形成链表
        struct dictEntry *next;
    
    } dictEntry;

    下面是字典的定义:

    type主要是针对不同的类型,private是针对函数的参数

    其中计算哈希值的函数就在type里面指向的

    有个哈希表数组,ht[1]只有rehash的时候使用,rehashindex也是rehash的时候使用

    typedef struct dict {
    
        // 类型特定函数
        dictType *type;
    
        // 私有数据
        void *privdata;
    
        // 哈希表
        dictht ht[2];
    
        // rehash 索引
        // 当 rehash 不在进行时,值为 -1
        int rehashidx; /* rehashing not in progress if rehashidx == -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, void *key);
    
        // 销毁值的函数
        void (*valDestructor)(void *privdata, void *obj);
    
    } dictType;

    当加入一个键值的时候,我们先根据type里面的函数计算出哈希值,&mask计算出索引值,加入哈希表的指定索引中,

    为了解决哈希表的冲突,我们使用拉链发,但是为了考虑效率,我们通常将新加入的节点放在最前面,不yongO(N)掺入

    rehash:

    哈希表的键值会不听的增多减少,为了让负载因子,维持在一个合理的范围,我们需要适当的进行扩展和收缩

    1. 为字典的 ht[1] 哈希表分配空间, 这个哈希表的空间大小取决于要执行的操作, 以及 ht[0] 当前包含的键值对数量 (也即是 ht[0].used属性的值):
      • 如果执行的是扩展操作, 那么 ht[1] 的大小为第一个大于等于 ht[0].used 2 的 2^n (2 的 n 次方幂);
      • 如果执行的是收缩操作, 那么 ht[1] 的大小为第一个大于等于 ht[0].used 的 2^n 2
    2. 将保存在 ht[0] 中的所有键值对 rehash 到 ht[1] 上面: rehash 指的是重新计算键的哈希值和索引值, 然后将键值对放置到 ht[1] 哈希表的指定位置上。
    3. 当 ht[0] 包含的所有键值对都迁移到了 ht[1] 之后 (ht[0] 变为空表), 释放 ht[0] , 将 ht[1] 设置为 ht[0] , 并在 ht[1] 新创建一个空白哈希表, 为下一次 rehash 做准备。

    哈希表的扩展和收缩的条件:

    1:如果没有执行BSAVE或者BGREWRITEAOF,并且负载因子大于等于1

    2:如果执行BSAVE或者BGREWRITEAOF,并且负载因子大于等于5

    这样设计是因为如果执行的话,会fork出新的进程,因为遵循写实复制,为了尽量避免写入内存进行复制,所以将负载因子提高一些

    如果负载因子小0.1执行收缩

    渐进事rehash:

    因为哈希表的数据可能特别的多,所有rehash不是一次完成的,是多次分批完成的,这里就用到了reashindex,最开始rehashindex=0,表示对索引值0指向的复制,结束了,开始索引值1的,rehashindx+1,这个过程中如果查找的话,会先查找ht[0]->ht[1],添加的话都会添加大1里面,这样可能保证服务器正常的运作

  • 相关阅读:
    方法和参数
    【转】priority_queue优先队列
    【转】主席树学习
    【转】树链剖分
    【转】线段树完全版~by NotOnlySuccess
    【转】树状数组
    【转】最大流EK算法
    【转】POJ题目分类推荐 (很好很有层次感)
    【转】原根
    【转】Polya定理
  • 原文地址:https://www.cnblogs.com/13224ACMer/p/7072093.html
Copyright © 2011-2022 走看看