redis源码学习-dict

Posted 孤独风中一匹狼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis源码学习-dict相关的知识,希望对你有一定的参考价值。

  • 1.字典相关的几个结构体
 dict由hash table存储key-value, hash table数组每一个元素存放dictEntry链接的链表头结点,dictEntry节点存放key-value
typedef struct dictEntry {
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;
} dictEntry;

typedef struct dictht {
    dictEntry **table;  // 指向dictEntry数组的指针
    unsigned long size; //哈希表table的大小,初始化大小为4
    unsigned long sizemask;  // size - 1 ,用来对hash值求与计算获得index
    unsigned long used;   // 已经赋值了的数量
} dictht;

typedef struct dict {
    dictType *type; // 方法
    void *privdata;  // 保存key和value
    dictht ht[2];   //  hash table
    long rehashidx; // 如果rehashidx=-1表示没有进行rehash,如果如果rehashidx>-1,则表示正在进行rehash,搬运的位置是rehashidx
    int iterators; /* number of iterators currently running */
} dict;

 

  • 2.动态扩容方法 int dictRehash(dict *d, int n)

   为了对dictht进行动态扩容,rehash方法将ht[0]中的值搬n个到ht[1]中, 分批次进行搬运,直到ht[0]中的值都搬到ht[1]上,再将ht[1]指针交给ht[0],rehashidx=-1,完成此次rehash过程

int dictRehash(dict *d, int n) {
    int empty_visits = n * 10; /* Max number of empty buckets to visit. */
    if (!dictIsRehashing(d)) return 0;

    // 从ht[0]中搬n个链表到ht[1]中
    while (n-- && d->ht[0].used != 0) {
        dictEntry *de, *nextde;

        /* Note that rehashidx can‘t overflow as we are sure there are more
         * elements because ht[0].used != 0 */
        assert(d->ht[0].size > (unsigned long) d->rehashidx);
        // 通过rehashidx可以接着从上一次搬完的位置开始搬
        while (d->ht[0].table[d->rehashidx] == NULL) {
            d->rehashidx++;
            if (--empty_visits == 0) return 1;
        }
        de = d->ht[0].table[d->rehashidx];
        /* Move all the keys in this bucket from the old to the new hash HT */

        // 把ht[0]上的一个链表搬到ht[1]上
        while (de) {
            unsigned int h;

            nextde = de->next;
            /* Get the index in the new hash table */
            h = dictHashKey(d, de->key) & d->ht[1].sizemask;
            de->next = d->ht[1].table[h];
            d->ht[1].table[h] = de;
            d->ht[0].used--;
            d->ht[1].used++;
            de = nextde;
        }
        d->ht[0].table[d->rehashidx] = NULL;
        d->rehashidx++;
    }

    /* Check if we already rehashed the whole table... */
    if (d->ht[0].used == 0) {
        zfree(d->ht[0].table);
        d->ht[0] = d->ht[1];
        _dictReset(&d->ht[1]);
        d->rehashidx = -1;
        return 0;
    }

    /* More to rehash... */
    return 1;
}

 

  • 3.使用到的几个hash算法

    1. 针对int的hash函数

       unsigned int dictIntHashFunction(unsigned int key) {
            key += ~(key << 15);
            key ^= (key >> 10);
            key += (key << 3);
            key ^= (key >> 6);
            key += ~(key << 11);
            key ^= (key >> 16);
            return key;
        }
    2. MurmurHash2算法

        unsigned int dictGenHashFunction(const void *key, int len) {
            /* ‘m‘ and ‘r‘ are mixing constants generated offline.
             They‘re not really ‘magic‘, they just happen to work well.  */
            uint32_t seed = dict_hash_function_seed;
            const uint32_t m = 0x5bd1e995;
            const int r = 24;
      
            /* Initialize the hash to a ‘random‘ value */
            uint32_t h = seed ^len;
      
            /* Mix 4 bytes at a time into the hash */
            const unsigned char *data = (const unsigned char *) key;
      
            // 长度大于等于4的情况
            while (len >= 4) {
                uint32_t k = *(uint32_t *) data;   // 4*8=32, 取4个字节当作uint32
      
                k *= m;
                k ^= k >> r;
                k *= m;
      
                h *= m;
                h ^= k;
      
                data += 4;
                len -= 4;
            }
      
            /* Handle the last few bytes of the input array  */
            // 剩下的长度小于4
            switch (len) {
                case 3:
                    h ^= data[2] << 16;
                case 2:
                    h ^= data[1] << 8;
                case 1:
                    h ^= data[0];
                    h *= m;
            };
      
            /* Do a few final mixes of the hash to ensure the last few
             * bytes are well-incorporated. */
            h ^= h >> 13;
            h *= m;
            h ^= h >> 15;
      
            return (unsigned int) h;
        }
    3. djb hash算法
  unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len) {

      unsigned int hash = (unsigned int) dict_hash_function_seed;
      while (len--)
          hash = ((hash << 5) + hash) + (tolower(*buf++)); /* hash * 33 + c */
      return hash;
  }   

 



细节前往

(
https://github.com/fangwendong/redis-learning/tree/master/struct/dict)

 

 




以上是关于redis源码学习-dict的主要内容,如果未能解决你的问题,请参考以下文章

redis学习记录:字典(dict)源码分析

redis源码学习-dict

redis源码分析----字典dict

redis源码阅读笔记----dict.c

redis 6源码解析之 dict

REDIS源码中一些值得学习的技术细节02