Redis系列之底层数据结构字典Dict
Dict数据结构
Dict是Redis数据结构中使用最为频繁的复合型数据结构,本质上是一个哈希表
查看redis6.0版本的源码,链接:https://github.com/redis/redis/blob/6.0/src/dict.h
哈希表的结构定义:
cpp
typedef struct dictht{
//哈希表数组
dictEntry **table;
//哈希表大小
unsigned long size;
//哈希表大小掩码,用于计算索引值,总是等于 size-1
unsigned long sizemask;
//哈希表已有节点的数量
unsigned long used;
}dictht
哈希表由数组table组成,table 中每个元素都是指向dictEntry这种数据结构,key用来保存键,val用来保存值,值可以是一个指针,也可以是uint64_t
整数,也可以是int64_t
整数
cpp
typedef struct dictEntry{
//键
void *key;
//值
union{
void *val;
uint64_tu64;
int64_ts64;
}v;
//指向下一个哈希表节点,形成链表
struct dictEntry *next;
}dictEntry
Dict在redis中的应用
在Redis中,Dict数据结构应该是使用最为频繁的复合型数据结构,除了在hash数据的会使用字典外,整个Redis的key和value也组成一个全局字典,Zset集合中存储value和score值的映射关系也是通过字典结构实现的
Redis的key和value映射使用dict
cpp
struct RedisDb{
dict* dict;// all keys key=>value
dict* expires;
...
}
zset中存储value和score值的映射关系,使用dict
cpp
struct zset {
dict *dict; // all values value=>score
zskiplist *zsl;
}
Dict如何解决hash冲突
dict本质也是一种key、value结构的哈希表,所以就有哈希冲突的问题,如果学过java中hashmap的数据结构,应该知道解决哈希冲突,常见的方法有开放地址法和链地址法。redis中的dict采用的是链地址法,也可以说使用链表来解决hash冲突。redis 中的dict通过next指针,可以将多个哈希值相同的键值对连接起来,用来解决哈希冲突
Dict拓展知识点
- redis中计算哈希值和索引值的方法
cpp
# 使用字典设置的哈希函数计算哈希值
hash = dict->type->hashFunction(key);
# 使用前面计算的哈希值hash和dict的sizemask计算索引值
index = hash & dict->ht[x].sizemask;
- dict的扩容和收缩,dict保存的键值对太多或者太少时,会触发
dictRehash
操作,重新散列来对哈希表进行扩容或者收缩。注意dict的rehash操作是渐进式的,因为如果键值对数量过多,要进行rehash操作是很耗时的,所以redis采用渐进式rehash,分多次、渐进式完成rehash操作 - hash冲突,redis dict哈希冲突的解决方法是采用链地址法,通过
*next
指针指向下一个具有相同索引值的hash表节点