redis笔记:哈希表 (转)

来自http://yulinlu.blog.163.com/blog/static/58815698201162361419296/

 

相关文件:

 Dict.c
关键数据结构如下
typedef struct dict { 
   dictType *type; 
   void *privdata; 
   dictht ht[2]; 
   int rehashidx; /* rehashing not in progress if rehashidx == -1 */ 
   int iterators; /* number of iterators currently running */ 
} dict; 
一个redis哈希表对应一个dict,dict里面有两个dictht(ht),每个dictht就是一个标准的哈希表。之所以要两个dictht是因为redis在发现哈希表需要扩容的时候,不是采取简单的直接分配新空间然后把内容复制过去的方法,它扩容的时候是分配一个新的哈希表ht[1],然后在往后的每次查询和更新的时候都把ht[0]的一个节点移动到ht[1],在扩容结束后ht[0]的内容为空,会被销毁然后把ht[1]覆盖到ht[0],ht[1]则初始化为初始值等待下一次扩容。
type存储了这个dict的算子,iterators表示这个哈希表当前正在使用的安全迭代器有多少个。rehashidx表示rehash到ht[0]的哪一个index,-1表示不需要rehash。redis的哈希表里面每次扩容就要rehash,rehash的操作就是把ht[0]的东西一点点放到ht[1]里面(见dictRehash)
/* This is our hash table structure. Every dictionary has two of this as we 
* implement incremental rehashing, for the old to the new table. */ 
typedef struct dictht { 
   dictEntry **table; 
   unsigned long size; 
   unsigned long sizemask; 
   unsigned long used; 
} dictht; 
dictht 中,table是主体内容,每个节点是一个dictEntry链表表头。size是table的大小,sizemask是size-1,用来计算求模的时候用的,used是哈希表里面已经使用了的大小。
typedef struct dictEntry { 
   void *key; 
   void *val; 
   struct dictEntry *next; 
} dictEntry; 
dictEntry链表是哈希表里面解决冲突的方式,两个不同的key如果经过hash后得到同一个index,他们就会存储在这个index对应的链表里,经典处理方式。
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; 
dict里面有一个dictType类型的type,其实就是让用户能自定义一大堆的算子而已
/* If safe is set to 1 this is a safe iteartor, that means, you can call 
* dictAdd, dictFind, and other functions against the dictionary even while 
* iterating. Otherwise it is a non safe iterator, and only dictNext() 
* should be called while iterating. */ 
typedef struct dictIterator { 
   dict *d; 
   int table, index, safe; 
   dictEntry *entry, *nextEntry; 
} dictIterator;
dictIterator 是哈希表的迭代器,table是dict 里面ht的索引,index是dictht 里面table的索引,entry是迭代器当前的dictEntry ,nextEntry是entry->next。比较特别的是safe,dictIterator 分为安全迭代器和非安全迭代器,非安全的迭代器只能在dictNext里用,安全的迭代器则可以在所有接口中使用。之所以有这个区别,还是因为redis的哈希表的逐步扩容的机制。如果产生了一个安全迭代器,dict的iterators就会加1,如果iterators不等于0这个哈希表不能被Rehash。这表示,如果这个哈希表正在扩容,他将停止从ht[0]挪动数据到ht[1]。
其实除去rehash外,其他东西都是标准的hash表,rehash看dictRehash就可以了
有问题的地方:
dictGetRandomKey极端情况可能会死循环(至少会卡CPU)
if (dictIsRehashing(d)) {
       do {
           h = random() % (d->ht[0].size+d->ht[1].size);
           he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] :
                                     d->ht[0].table[h];
       } while(he == NULL);
   } else {
       do {
           h = random() & d->ht[0].sizemask;
           he = d->ht[0].table[h];
       } while(he == NULL);
   }
这一段,没有设置上限,如果random一直没命中,就会一直尝试下去

 

你可能感兴趣的:(redis,数据结构,数据库,开源软件,哈希表)