哈希表是面试一种存储数据的结构,他有很多名字(键值对、官谈字典、谈对符号表、希表映射、关联数组)。在哈希表中,键和值是一一对应的关系,一个键key对应一个值value。哈希表这个数据结构可以通过键key,在O(1)时间复杂度的情况下获得对应的值。
由于C语言自己没有内置哈希表这一数据结构,因此Redis自己实现了Hash表。
哈希冲突及处理办法
哈希表最关键的问题就在于哈希冲突。即,两个项,经过哈希函数计算,发现其对应的存储方式位置一致。对于这种情况,就需要进行进一步处理了。
解决哈希冲突的办法
大家应该背过我写的数据结构与算法八股文背诵版,还记得解决Hash冲突的方法嘛。
线性探查法(开放地址)。
这个方法的核心是:一旦碰见有冲突,该项往后顺延.
来看个例子吧。
1.按hash算法,新键值对应该存在箭头所处位置,可惜该位置有值了:
开放地址法
2.因此需要存储顺延的位置:
开放地址法
3.顺延位置也有值了,再往后顺延
开放地址法
4.顺延位置还是有值,再往后顺延,终于存储上了
开放地址法
链地址法(拉链法)
Redis采用的方法就是这种拉链法。来看下面例子。新键值对计算应该存到二号,二号此时已经有一个键值对了。因此,直接通过链表的方式挂到二号键值对1的下面。
拉链法
对于新的键值对也是如此,通过链表的方式挂到二号键值对2的下面。
Rehash
在讲rehash之前,首先需要引入一个定义:负载因子。来看一下负载因子的定义吧:
负载因子 = 散列表内元素个数/散列表的长度
如果负载因子高,就说明哈希冲突概率大,这样会严重拖慢查找效率。
如果负载因子低,就说明这哈希表好像占用空间太多了,大部分空间都没元素。
为了使负载因子值在合理范围内,程序需要对哈希表进行扩展或收缩。由于空间变大或缩小,之前的键在老表的存储位置,在新表中就不一定一样了,需要重新计算。这个重新计算,并把老表元素转移到新表元素的过程就叫做rehash。当然无论是java中的hashmap,concurrenthashmap,还是今天要讲的Redis哈希表,都涉及rehash过程。
来看一下Redis的Hash表逻辑设计结构 Redis的哈希表主要由三个结构构成:
dictht。单纯表示一个哈希表
dictEntry。哈希表的一项,可以看作就是一个键值对
dict。Redis给外层调用的哈希表结构,包含两个dictht
- typedef struct dictht {
- dictEntry **table; //哈希表数组(哈希表项集合)
- unsigned long size; //Hash表大小
- unsigned long sizemask; //哈希表掩码
- unsigned long used;//Hash表已使用的大小
- } dictht;
稍微解释一下各个项。
讲了Hash表,来看看哈希项
- typedef struct dictEntry {
- void *key;
- union {
- void *val;
- uint64_t u64;
- int64_t s64;
- double d;
- } v;
- struct dictEntry *next;
- } dictEntry;
我们知道,Redis采用拉链法解决哈希冲突的问题。因此,Redis的哈希表项就有一个next指针,指向下一个元素,通过该指针,就可以访问多个具有相同哈希值的键值对。
最后我们来看看dict结构。
- typedef struct dict {
- dictType *type;
- void *privdata;
- dictht ht[2];
- int reshaidx;
- } dict;
大家肯定很好奇,好好的dict,搞两个哈希表做啥?当然也有不好奇的小伙伴,但没办法,架不住面试官也很好奇啊。
答案揭晓,两个hash表是为了rehash。
那什么情况下需要rehash呢?
我们来看一下如果出现需要rehash的情况,需要的执行步骤:
由于步骤二采用的计算方式如果在一定时间做,占用资源过高,所以redis提出了渐进式rehash的方式。拿大白话来讲,就是原来是一次,一次性的搬运,现在变成了分批搬运。
在分批搬运的过程中,难免会收到其他各式各样的请求。
参考
https://www.cnblogs.com/tekkaman/p/5141936.html
https://blog.csdn.net/yangbodong22011/article/details/78467583
Redis的设计与实现
Redis源码剖析与实战
责任编辑:武晓燕 来源: 后端技术小牛说 Redis哈希表存储
(责任编辑:探索)
国联证券(601456)中签率与中签号查询?国联证券何时能申购
冠豪高新(600433.SH):重组事项获有条件通过 公司A股股票自3月12日起复牌
南京银行(601009.SH)拟发行不超400亿元金融债券 一次或分次申报
海信电视平均出货尺寸跃居全球第一 在超大屏幕市场上也已强势取胜