zoukankan      html  css  js  c++  java
  • Redis设计与实现 (三): 字典

     哈希表

    结构定义dict.h/dictht   

    /*
    * 哈希表
    *
    * 每个字典都使用两个哈希表,从而实现渐进式 rehash 。
    */
    typedef struct dictht {
    
     // 哈希表数组
     dictEntry **table;
    
     // 哈希表大小
     unsigned long size;
    
     // 哈希表大小掩码,用于计算索引值
     // 总是等于 size - 1
     unsigned long sizemask;
    
     // 该哈希表已有节点的数量
     unsigned long used;
    
    } dictht;

    table是一个数组, 每个元素是一个指向dict.h/dictEntry 结构的指针.

    哈希表节点

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

    next属性指向下一个节点, 形成链表, 解决哈希冲突.

    字典

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

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

    /*
    * 字典类型特定函数
    */
    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;

    ht数组包含两个哈希表,  一般情况下操作ht[0],  ht[1] 只有在ht[0]哈希表进行rehash时使用.  rehashidx, 记录了rehash的进度, 没有则为 -1 ;

    哈希算法

    Redis 使用MurmurHash2算法计算哈希值

    解决键冲突

    链地址法,  采用头插法,  时间复杂度为O(1).

    rehash

    1. 为 ht[1] 分配空间 : 如果是扩容,  分配大小为 第一个大于等于 ht[0] .used * 2 的 2的n次幂; 如果是收缩,  分配空间大小为  第一个大于等于 ht[0] .used 的 2的n次幂.

    2.  将ht[0] 上的键值对 通过重新计算键的哈希值和索引重新分配到ht[1] 上.

    3. 这时ht[0] 将变为空表,  释放ht[0],  将ht[1] 设置为ht[0],  并在ht[1]创建一个空表, 为下次rehash做准备.

    渐进式rehash

    在字典维持一个索引计数器 rehashidx, 设置为0, 表示正式工作.

    在对字典执行添加, 删除, 查找或修改时,  将 rehashidx 索引上的所有键值对 rehash 到 hd[1], 完成后 rehashidx++;

    当ht[0] 上的所有键值对被 rehash到 ht[ 1 ], 设置 rehashidx = - 1, 表示操作已经完成了.

    在此期间操作数据, 会使用ht[0] 和 ht[1]两个hash表,  例如要找某个键值, 会先在ht[0] 找, 没有就去ht[ 1 ] 找.  新增操作都保存到ht[ 1 ] 中. 保证了ht[0] 只减不增, 最终为空.

  • 相关阅读:
    CSS简要内容
    HTML简要内容
    java中的多线程
    java中的IO流
    hash
    java中的集合
    java中的数组
    java中的异常机制(编译时异常)
    静态变量和静态方法的访问权限
    成员变量和成员方法的访问权限
  • 原文地址:https://www.cnblogs.com/tanxing/p/6715002.html
Copyright © 2011-2022 走看看