名词介绍:
字典:顾名思义,能根据响应的索引找到值的一种数据结构。字典又称为符号表(symbol table),关联数组(associative array),或者映射(map),是一种用于保存键值对(key-value)的抽象数据结构,一个字典中的键(key)是唯一的,能根据唯一的键找出相对应的值。在redis这种典型的key-value的内存型存储系统,自然也是使用了字典作为其底层数据结构。
Redis中字典的实现
Hash表
redis中的Hash表和Java语言中的Map数据结构类似,实际都是采用数组+链表的,链地址法来进行hash的;
redis中定义的Hash表由dict.h/dictht结构定义:
typedef struct dictht{
//哈希表数组
dictEntry **table;
//hash表大小
unsigned long size;
//hash表大小掩码,用于计算索引值
unsigned long sizemask;
//hash表已用的节点数量
unsigned long used;
}dictht;
哈希表节点
哈希表节点使用dictEntry结构表示,每个dictEntry结构都保存着一个键值对:
typedef struct dictEntry{
//key
void *key;
//value
union{
void *value;
uint64_t u64;
int64_t s64;
}v;
//指向下一个hash表的几点,形成链表
struct dictEntry *next;
}
redis中使用解决hash冲突的方式是:链地址法
所谓链地址法,如果两个数据的hash值相同,则将后插入的数据以链表的形式插入到前一个后面,以此形成一个链表,所以叫链地址法。
字典
在redis中字典是由dict.h/dict数据结构表示
typedef struct dict{
//类型特定函数
dictType *type;
//私有数据
void *privatedata;
//hash表
dictht ht[2];
//rehash索引,当rehash不存在时候,值为-1
int trehsshidx;
}
这里需要解释一下,dict中的hash表,从结构中我们可以看出,hash表的大小为2,
因为当一个hash表中元素达到一定的数量的时候,需要对hash表进行扩容。则需要将原来的元素重新进行hash。
redis中计算索引的方法:
1:计算key的hash值
hash = dict->type->hashFunction(key);
2:计算索引:使用hash表的sizemask属性和哈希值,计算出索引值。
index = hash& dict->ht[x].sizemask;
redis中rehash
- 随着操作的不断执行,哈希表中保存的键值对会增加或者减少,程序需要对hash表进行对应的扩展或者收缩。
- rehash的步骤
(1)为字典的ht[1]哈希表分配空间
若是扩展操作,那么ht[1]的大小为>=ht[0].used*2的2^n
若是收缩操作,那么ht[1]的大小为>=ht[0].used的2^n
(2)将保存在ht[0]中的所有键值对rehash到ht[1]中,rehash指重新计算键的哈希值和索引值,然后将键值对放置到ht[1]哈希表的指定位置上。
(3)当ht[0]的所有键值对都迁移到了ht[1]之后(ht[0]变为空表),释放ht[0],将ht[1]设置为ht[0],新建空白的哈希表ht[1],以备下次rehash使用。
注意:redis中hash不是一次性完成的,如果数据量特别大,需要计算很大数据量的hash值,这将很耗费cpu和系统资源,可能造成系统短时间不可用。所以系统会分批对hash值进行计算重新放入另外一个hash表;