Redis~哈希(Hash)类型的数据结构之字典(1)

文章详细介绍了Redis中哈希表的数据结构,包括dictEntry结构、哈希函数、键值对存储与解决键冲突的链地址法。还阐述了Redis如何通过双哈希表和渐进式rehash来应对高负载和高效能需求。
摘要由CSDN通过智能技术生成

//哈希表数组

dictEntry **table;

//哈希表大小

unsigned long size;

//哈希表大小掩码,用于计算索引值。总是等于size-1

unsigned long sizemask;

//该哈希表已有节点的数量

unsigned long used;

} dictht;

成员介绍:

  • table属性:是一个数组,数组中的每个元素都是一个指向dict.h/dictEntry结构的指针,每个 dictEntry结构保存着一个键值对

  • size属性:记录了哈希表的大小,也即是table数组的大小

  • used属性:则记录了哈希表目前已有节点(键值对)的数量

  • sizemask属性:的值总是等于size-1,这个属性和哈希值一起决定一个键应该被放到table数组的哪个索引上面

  • 下图展示了一个大小为4的空哈希表(没有包含任何键值对)

在这里插入图片描述

哈希表节点

  • 哈希表节点使用dictEntry结构表示,每个dictEntry结构都保存着一个键值对:

typedef struct dictEntry {

//键

void *key;

//值

union{

void *val;

uint64_tu64;

int64_ts64;

} v;

//指向下个哈希表节点,形成链表

struct dictEntry *next;

} dictEntry;

成员如下:

  • key属性保存着键值对中的键

  • v属性则保存着键值对中的值,值可以是 一个指针,或者是一个uint64_t整数,又或者是一个int64_t整数

  • next属性是指向另一个哈希表节点的指针,这个指针可以将多个哈希值相同的键值对连 接在一次,以此来解决键冲突(collision)的问题

  • 下图就展示了如何通过next指针,将两个索引值相同的键k1和k0连接在一 起在这里插入图片描述

  • 因为dictEntry节点组成的链表没有指向链表表尾的指针,所以为了速度考虑,程序总是 将新节点添加到链表的表头位置(复杂度为O(1)),排在其他已有节点的前面。

字典(struct dict)

typedef struct dict {

//类型特定函数

dictType *type;

//私有数据

void *privdata;

//哈希表

dictht ht[2];

//rehash 索引。当rehash 不在进行时,值为-1

in trehashidx; /* rehashing not in progress if rehashidx == -1 */

} dict;

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;

  • 为保证字典具有多态及泛型,dictType中提供了如哈希函数以及K-V的各种操作函数,使得字典适用于多重情景

  • 下图展示了一个普通状态下(没有进行rehash)的字典

在这里插入图片描述

  • 当要将一个新的键值对添加到字典里面时,程序需要先根据键值对的键计算出哈希值和索引值,然后再根据索引值,将包含新键值对的哈希表节点放到哈希表数组的指定索引上 面

#使用字典设置的哈希函数,计算键key 的哈希值, Redis使用MurmurHash2算 法来计算键的哈希值

hash = dict->type->hashFunction(key);

#使用哈希表的sizemask 属性和哈希值,计算出索引值

#根据情况不同,ht[x] 可以是ht[0] 或者ht[1]

index = hash & dict->ht[x].sizemask;

  • Redis的哈希表使用链地址法(separate chaining)来解决键冲突,每个哈希表节点都有 一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上 的多个节点可以用这个单向链表连接起来,这就解决了键冲突的问题

rehash(重新排列)


  • Redis作为一个插入频繁且对效率要求高的数据库,当插入的数据过多时,就会因为哈希表中的负载因子过高而导致查询或者插入的效率降低,此时就需要通过rehash来进行重新扩容并重新映射。

  • 但是如果只是用一个哈希表,映射时就会导致数据库暂时不可用,作为一个使用频繁的数据库,短期的停机几乎是不可容许的问题,所以Redis设计时采用了双哈希的结构,并采用了渐进式rehash的方法来解决这个问题。

rehash双哈希结构的实现

  • rehash双哈希结构实现步骤如下
  1. 为ht[1]的哈希表分配空间
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

金三银四到了,送上一个小福利!

image.png

image.png

专题+大厂.jpg
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
三银四到了,送上一个小福利!

[外链图片转存中…(img-PDD1gCBl-1713276714951)]

[外链图片转存中…(img-0L14DyBy-1713276714951)]

[外链图片转存中…(img-YBxp8TEH-1713276714951)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值