Redis之Hash数据结构
2015-10-28 21:47
519 查看
0.前言
redis是KV型的内存数据库, 数据库存储的核心就是Hash表, 我们执行select命令选择一个存储的db之后, 所有的操作都是以hash表为基础的, 下面会分析下redis的hash数据结构和实现.1.hash数据结构
/*Hash表一个节点包含Key,Value数据对 */ typedef struct dictEntry { void *key; union { void *val; uint64_t u64; int64_t s64; double d; } v; struct dictEntry *next; /* 指向下一个节点, 链接表的方式解决Hash冲突 */ } dictEntry; /* 存储不同数据类型对应不同操作的回调函数 */ 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; typedef struct dictht { dictEntry **table; /* dictEntry*数组,Hash表 */ unsigned long size; /* Hash表总大小 */ unsigned long sizemask; /* 计算在table中索引的掩码, 值是size-1 */ unsigned long used; /* Hash表已使用的大小 */ } dictht; typedef struct dict { dictType *type; void *privdata; dictht ht[2]; /* 两个hash表,rehash时使用*/ long rehashidx; /* rehash的索引, -1表示没有进行rehash */ int iterators; /* */ } dict;
2.hash数据结构图
![](http://images2015.cnblogs.com/blog/339657/201510/339657-20151020231754692-188838154.png)
3.渐进式hash说明
dict中ht[2]中有两个hash表, 我们第一次存储数据的数据时, ht[0]会创建一个最小为4的hash表, 一旦ht[0]中的size和used相等, 则dict中会在ht[1]创建一个size*2大小的hash表, 此时并不会直接将ht[0]中的数据copy进ht[0]中, 执行的是渐进式rehash, 即在以后的操作(find, set, get等)中慢慢的copy进去, 以后新添加的元素会添加进ht[0], 因此在ht[1]被占满的时候定能确保ht[0]中所有的数据全部copy到ht[1]中.4.创建hash表
创建hash表过程非常简单,直接调用dictCreate函数, 分配一块内存,初始化中间变量即可.dict *dictCreate(dictType *type, void *privDataPtr) { /*分配内存*/ dict *d = zmalloc(sizeof(*d)); /*初始化操作*/ _dictInit(d,type,privDataPtr); return d; }
5.添加元素
hash表中添加元素,首先判断空间是否足够, 然后计算key对应的hash值, 然后将需要添加的key和value放入表中.int dictAdd(dict *d, void *key, void *val) { /*添加入hash表中, 返回新添加元素的实体结构体*/ dictEntry *entry = dictAddRaw(d,key); if (!entry) return DICT_ERR; /*元素val值放入元素实体结构中*/ dictSetVal(d, entry, val); return DICT_OK; } /* *添加元素实体函数 */ dictEntry *dictAddRaw(dict *d, void *key) { int index; dictEntry *entry; dictht *ht; if (dictIsRehashing(d)) _dictRehashStep(d); /*根据key值计算新元素在hash表中的索引, 返回-1则表示元素已存在, 直接返回NULL*/ if ((index = _dictKeyIndex(d, key)) == -1) return NULL; /*如果在进行rehash过程,则新元素添加到ht[1]中, 否则添加到ht[0]中 */ ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0]; entry = zmalloc(sizeof(*entry)); entry->next = ht->table[index]; ht->table[index] = entry; ht->used++; /*设置元素key*/ dictSetKey(d, entry, key); return entry; } /* *计算索引的函数 */ static int _dictKeyIndex(dict *d, const void *key) { unsigned int h, idx, table; dictEntry *he; /* 判断hash表是否空间足够, 不足则需要扩展 */ if (_dictExpandIfNeeded(d) == DICT_ERR) return -1; /* 计算key对应的hash值 */ h = dictHashKey(d, key); for (table = 0; table <= 1; table++) { /*计算索引*/ idx = h & d->ht