• 第四章 字典


    在字典中,一个键(key)可以和一个值(value)进行关联(或者说将键映射为值),这些关联的键和值就称为键值对。

    4.1 字典的实现

      Redis的字段使用哈希表作为底层实现。

      4.1.1 哈希表

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

      table是一个数组,其中每个元素是指向一个哈希表节点的指针。

      size记录了table的大小。sizemask和哈希值一起决定一个键应该被放到table数组的哪个索引上。

      4.1.2 哈希表节点

      每个dictEntry结构保存着一个键值对。其中值可以是一个指针,或者uint64_t整数,或者int64_t整数。next属性指向另一个哈希表节点的指针,这个指针可以将多个哈希值相同的键值对连接在一起,解决哈希冲突的问题。

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

      4.1.3 字典

      Redis中的字典结构:

    typedef struct dict{
        //类型特定函数
        dictType *type;
    
        //私有数据
        void *privdata;
    
        //哈希表
        dicht ht[2];
    
        //rehash 索引,当rehash不在进行时,值为-1  
        int trehashidx;
    }dict

      type和privdata是为了多态字典而设计的,其中type指向了一个dictType结构,里面定一个了一系列操作键值对的函数,而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属性是一个包含两个项的数组,数组中的每个项都是一个dictht哈希表,一般情况下,字典只使用ht[0]哈希表,ht[1]哈希表只会在rehash时使用,rehashidx记录了rehash的进度。

    4.2 哈希算法

      先计算key的哈希值,根据哈希值和sizemask计算索引值。根据索引值将键存放到指定位置。

    4.3 解决键冲突

      当有两个及以上的键被分到同一个索引上时,产生了哈希冲突,Redis的解决方法是链地址法,将冲突的部分排成链表。因为没有指向链表表尾的指针,出于速度考虑,最新插入的放在表头位置。

    4.4 rehash

    1. 为ht[1]分配空间
      1. 如果是扩展操作,取第一个大于等于ht[0].used*2的2的n次方幂
      2. 如果是收缩操作,取第一个大于等于ht[0].used的2的n次方幂
    2. 将ht[0]中的键值对重新映射到ht[1]中
    3. 将ht[0]空间释放,将ht[1]设置为h[0],将ht[0]设置为ht[1],并在ht[1]处创建一个新的空白哈希表,为下次rehash做准备

    4.5 渐进式rehash

      渐进是指当键值对太多的情况,并不会一次全部重新分配完。

    1. 为ht[1]分配空间,字典同是持有ht[0]和ht[1]两个哈希表
    2. 将rehashidx初始化为0
    3. 每次对字典执行增删改查操作时,程序还会额外将ht[0]表在rehashidx索引上所有的键值对rehash到ht[1]中,当操作完毕时,将rehashidx+1
    4. 最终,ht[0]的所有键值对全部迁移完,将rehashidx重置为-1

      在渐进rehash期间,新增的键值对,一律保存在ht[1]中。删改查则先在ht[0]中查找,未找到再去ht[1]中查找。

    人生就像蒲公英,看似自由,其实身不由己。
  • 相关阅读:
    解决CentOS 7 history命令不显示操作记录的时间和用户身份问题
    CentOS7关闭selinux
    Centos7下添加开机自启动服务和脚本
    快速查看一个文件的权限 stat -c %a
    修改centos7系统语言
    sudo
    chsh命令 修改用户登录shell
    忘记root开机密码及怎样开启密码远程连接模式
    centos7系统中添加 pstree 命令
    vim 多行添加注释,取消注释
  • 原文地址:https://www.cnblogs.com/walker993/p/14425825.html
Copyright © 2020-2023  润新知