redis学习-字典

基本说明

  1. 字典,是一种用于保存键值对的抽象数据结构。
  2. 字典在redis中的应用相当广泛,
    1. 比如redis的数据库就是使用字典来作为底层实现。
      比如我执行命令:
      set msg “hello world”
      实际上就是保存在数据库的字典里面的。
    2. 哈希键的底层实现也是字典。

字典的实现

Redis的字典使用哈希表作为底层实现,一个哈希表里面有很多哈希表节点,每个哈希表节点就保存了字典中的一个键值对。

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


  1. 每个哈希表节点都保存一个键值对
  2. key表示键,而v属性则保存着键值对中的值,其中键值对的值可以是一个指针,或者是一个uint64_t整数,又或者是一个int64_t整数(共占一段最大的内存)。
哈希表结构
typedef struct dictht{
    //哈希表数组
    dictEntry **table;
    
    //哈希表的大小
    unsigned long size;
    
    //哈希表大小掩码,用于计算索引值,总是等于size-1
    unsigned long sizemask;
	//哈希表已有节点的数量
	unsigned long used;
}dictht;
  1. table属性是一个数组,每一个指向一个dict.h/dictEntry结构的指针,每个dictEntry结构保存一个键值对和一个指向下一个键值对的指针。
  2. size记录了哈希表的大小,即table数组的大小。
  3. used记录了哈希表目前已有节点的数量
字典结构
typedef struct dictType{
    //计算哈希值的函数
    unsigned int (*hashFuntion)(const void *key);
    
    //复制键的函数
    void *(*keyDup)(void *privdata,const void *key);
    
    //复制值的函数
    void *(*valDup)(void *privdata,const void *obj);
    
    //对比键的函数
    void (*keyCompare)(void *privdata,const void *key1,const void *key2);
    
    //销毁键的函数
    void (*keyDestructor)(void *privdata,const void *key1,const void *key2);
    
    //销毁值的函数
    void (*valDestructor)(void *privdata,void *obj);
    
}dictType;

typedef struct dict{
    //类型特定的函数
    dictType *type;
    
    //私有数据
    void *privdata;
    
    //哈希表
    dictht ht[2];
    
    //refresh索引,当refresh不在进行时,值为-1
    int trehashidx;
}dict;
  1. type属性是一个指向dictType结构的指针,每一个dictType结构保存了一簇用于操作特定键值对的函数,redis会为用途不同的字典设置不同的类型特定的函数。
  2. privdata属性则保存了需要传给那些特定函数的可选参数。
  3. ht保存两个项的数组,数组中的每个项都是一个dictht哈希表,一般情况下,字典只使用ht[0]哈希表,ht[1]哈希表只会在对ht[0]哈希表进行rehash时使用
  4. rehashidx记录了rehash的进度,如果目前没有在进行rehash,那么它的值为-1.

哈希算法

  1. 将一个新的键值对添加字典里面,首先是用hash算法计算哈希值和索引值:
    hash = dict->type->hashFunction(k0);
  2. 假设计算出的hash值为8,那么程序会继续使用语句:
    index = hash&dict->ht[0].sizemask = 8 & 3 = 0

refresh操作

  1. 为字典ht[1]哈希表分配空间,空间大小为第一个大于等于ht[0].used*2的2^n
  2. 然后将ht[0]上的值都映射到th[1]中,之后释放ht[0],再把ht[0]指向ht[1],ht[1]指向空白哈希表

渐进式refresh

当refresh时,不并不是一下子转移的,比如当键值对太多的时候,这个时候就需要使用标志rehashhidx,步骤如下:

  1. 为ht[0]分配空间,让字典同时次优ht[0]和ht[1]两个哈希表
  2. 在字典维持一个索引计数器变量rehashidx,并将它的值设置为0,表示rehash工作正式开始
  3. 在rehash进行期间,每次对字典执行添加,删除,查找或者更新操作时,程序除了执行指定的操作以外,还会顺带将rehashidx索引值加一,代表已经转移到第几个键值对数组了
  4. 全部转移完成后,rehashidx值变为-1

在rehash操作的期间,如果有增删改查,就会先在ht[0]查找,如果没有再到ht[1]中寻找。

你可能感兴趣的:(Redis学习,redis)