您的位置:首页 > 数据库 > Redis

算法导论原理分析系列5:第11章 散列表

2013-11-13 00:00 239 查看
这个正好之前看的redis源码里有用到哈希表,所以结合redis源码来分析哈希表。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~先看书上的理论

11.1 直接寻址表

简单,略过。

11.2 散列表

首先需要计算某种哈希算法,算出key对应的hash值,然后放在这个桶里面去,可以采用链表方式。

11.3散列函数

一般这个函数就自己计算了,

书上罗列出来的有

11.3.1 除法散列法

11.3.2乘法散列法

11.3.3 全域散列

11.4 开放寻址法

有三种技术: 线性探查、二次探查、双重探查。

本质就是:若当前的槽已经有数据了,那么再采用新的计算方法。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~比对redis源码来进行实际训练

1 redis创建哈希表

源码位于:

void initServer() 下的

/* Create the Redis databases, and initialize other internal state. */
for (j = 0; j < server.dbnum; j++)
{
server.db[j].dict = dictCreate(&dbDictType,NULL);

注意,每个db里有好几个哈希表,这里选择dict哈希表进行讲解创建过程。

进入dictCreate函数查看源码。

/* Create a new hash table */
//dictCreate(&commandTableDictType,NULL);
dict *dictCreate(dictType *type, void *privDataPtr)
{
dict *d = zmalloc(sizeof(*d));
_dictInit(d,type,privDataPtr);
return d;
}

主要就是函数_dictInit,跟进去看。

/* Initialize the hash table */
int _dictInit(dict *d, dictType *type,
void *privDataPtr)
{
_dictReset(&d->ht[0]);
_dictReset(&d->ht[1]);
...

//其它代码

}

注意:这里涉及到2个哈希表。下面会说明。

所以又要去关注_dictReset函数。

static void _dictReset(dictht *ht)
{
//自定义检查点: 1 2 3
ht->table = NULL;
ht->size = 0;
ht->sizemask = 0;
ht->used = 0;
//自定义检查点: 1 2 3
}

到目前为止,一个哈希表的基本结构就有了,但是没有存储空间。

注意,相关的一个结构是

/* Db->dict, keys are sds strings, vals are Redis objects. */
dictType dbDictType = {
dictSdsHash, /* hash function */
NULL, /* key dup */
NULL, /* val dup */
dictSdsKeyCompare, /* key compare */
dictSdsDestructor, /* key destructor */
dictRedisObjectDestructor /* val destructor */
};
定义了dict哈希表相关的各个函数,包括哈希函数,key,val复制函数及比较函数。

2[b] redis对哈希表进行扩容[/b]

通常情况下,数据会放在第一个哈希表里,也就是ht[0],只有在rehash的时候才会用ht[1].

具体原理我没看,参照:http://www.searchdatabase.com.cn/showcontent_65072.htm

3加入数据到哈希表

主要步骤在dictAddRaw函数里。

/* Get the index of the new element, or -1 if
* the element already exists. */
if ((index = _dictKeyIndex(d, key)) == -1)
return NULL;
//计算出哈希值

/* Allocate the memory and store the new entry */
ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
//获取哈希表,2选1

entry = zmalloc(sizeof(*entry));
//分配变量的存储空间

entry->next = ht->table[index];

ht->table[index] = entry;
ht->used++;
//以链表方式加入,复杂度O(1)

/* Set the hash entry fields. */
dictSetKey(d, entry, key);

//设置key.

dictSetKey是一个宏定义如下:

#define dictSetKey(d, entry, _key_) do { \
if ((d)->type->keyDup) \
entry->key = (d)->type->keyDup((d)->privdata, _key_); \
else \
entry->key = (_key_); \
} while(0)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息