redis数据结构 字典 哈希表 用于hash键底层实现 总结

1.原理
字典是哈希键的底层实现,当一个哈希键包含的键值对比较多,或者键值对中元素都是比较长的字符串,Reids就会使用字典作为哈希键的底层实现

2.字典节点结构

typedef struct dictEntry {
void *key //键
union {
      void *val;
      unit64_t u64;
      int64_t s64;
}v;//值
struct dictEntry *next; // 指向下一个哈希表节点 形成链表
}dictEntry;

字典节点也是链表结构,用于哈希冲突后形成链表

3.字典结构

typedef struct dict {
dictType *type //类型特定函数
void *privdata是// 私有数据
dictht ht[2]; //哈希表
int trehashidx; // rehash索引 当rehash不在进行时 值位-1
}dict;

type属性和privdata属性针对不同类型的键值对,为创建多态字典而设置的
dictType结构里面封装了各自方法
ht[2]是一个包含两个哈希表的数组,一般情况,字典只使用ht[0]哈希表,ht[1]哈希表只会在对ht[0]哈希表进行rehash时候使用,即在对字典扩容的时候使用

4.dictType结构

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 (*valDeestructor) (void *privdata, void *bje) //销毁值的函数
}dictType;

5.哈希算法
添加一个新的键值对到字典里面时,根据键值对计算出哈希值和索引值,然后根据索引值,把包含新键值对的哈希表节点放到哈希表数组指定索引上

hash = dict->type->hashFunction(key) 使用字典设置的哈希函数,计算键key的哈希值,然后使用哈希表的sizemask属性和哈希值,计算出索引值
index = hash & dict->ht[x]->sizemask;

6.解决键冲突
Redis的哈希表使用链地址法来解决键冲突,因为dictEntry节点组成的链表没有指向链表表尾的指针,为了速度考虑,总是将新节点添加到链表的表头位置

7.rehash
随着操作的不断执行,哈希表保存的键值对会逐渐地增多或减少,为了让哈希表的负载因子维持维持在一个合理的范围之内,当哈希表保存的键值对数量太多或太少,程序需要对哈希表的大小进行相应的扩展或收缩

8.扩容和缩容操作
扩展和收缩哈希表,可以通过执行rehash作来完成

  1. 为字典的ht[1]哈希表分配空间,大小取决于要执行的操作,以及ht[0]当前包含的键值对数量
    扩展操作 设置ht[1]大小为第一个2倍ht[0]已使用空间大的2的次幂
    收缩操作 设置ht[1]大小为第一个二分之一ht[0]已使用空间小的2的次幂
  2. 将保存在ht[0]中是所有键值对rehash到ht[1]上面,rehash指的是重新计算键的哈希值和索引值,将键值对放置到,ht[1]哈希表指定位置上
  3. 当ht[0]包含的所有键值对都迁移到了ht[1]之后 ht[0]变为空表,释放ht[0] 将ht[1]设置为ht[0],并在ht[1]新创建一个空白hash表,为下一次rehash做准备

8.扩容和缩容操作时机
负载因子 = 哈希表已经保存节点数量 / 哈希表大小

  1. 服务器目前没有执行BGSAVE命令或BGREWRITEAOF, 并且负载因子大于1
  2. 服务器目前正在执行BGSAVE命令或者BGREWRITEAOF,并且负载因子大于5
  3. 当负载因子小于0.1时程序自动开始对哈希表执行收缩操作

9.渐进式rehash
扩展或收缩哈希表需要将ht[0]里面的所有键值对rehash到ht[1],但是这个rehash动作并不是一次、集中式地完成的,而是分多次、渐进式完成的

10.渐进式rehash步骤

  1. 为ht[1]分配空间在字典中维持一个索引计数器变量 rehashidx 并将它的值设置为0,表示rehash工作正式开始
  2. 进行rehash期间,每次对字典执行添加、删除、查找或者更新操作,程序除了执行指定的操作,还会顺带将ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1]中,当rehash工作完成后,程序将rehashidx属性的值增一,随着字典操作不断执行,最终在某个时间点 ht[0]的所有键值对都被rehash至ht[1],这时程序将rehashidx属性设置为-1,表示rehash操作已经完成,渐进式rehash的好处在于它采取分而治之的方式,将rehash键值对所需的计算工作均摊到字典的每个添加、删除、查找和更新,操作上,从而避免了集中式rehash而带来的庞大计算量

11.渐进式rehash执行期间的哈希表操作
在渐进式rehash操作中,字典会同时使用ht[0]和ht[1]两个哈希表,所以在渐进式rehash进行期间,字典的删除(delete)、查找(find)、更新(update)等操作会在两个哈希表上进行。例如在字典上查找一个键如果在ht[0]没有找到,就去ht[1]找在渐进式rehash执行期间,新添加到字典的键值对一律会保存到ht[1]里面而ht[0]不执行任何添加操作,这一措施保证了ht[0]包含的键值对数量会只减少不增加,并随着rehash操作的执行而最终变成了空表

12.总结
1.字典被广泛用于实现Redis的各种功能,包括数据库和哈希表
2.Redis中的字典使用哈希表作为底层实现,每个字典带有两个哈希表,一个平时使用,一个仅在进行rehash使用
3.当字典被用作数据库的底层实现,或者哈希键的底层实现,Reids使用 MurmurHash2算法来计算键的哈希值
4.哈希表使用链地址法来解决键冲突,被分配到同一个索引上的多个键值对会连接成一个单向链表
5.在对哈希表进行扩展或收缩操作时,程序需要将现有哈希表包含的所有键值对对rehash到新哈希表里面,并且这个rehash过程并不是一次性地完成的,而是渐进式地完成的

参考
《Redis设计与实现》

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Redis使用了多种数据结构来存储数据,这些数据结构包括字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set)等。下面我将为你简单解释每种数据结构底层实现方式。 1. 字符串(String):字符串是Redis最基本的数据结构,它的底层实现使用了简单动态字符串(SDS)。SDS是一种可变长度的字符数组,它允许进行高效的增删改操作,并且支持常数时间复杂度的读取操作。 2. 哈希(Hash):哈希是一种值对的集合,类似于其他编程语言中的字典或映射。Redis中的哈希底层实现使用了哈希表Hash Table)。哈希表通过将映射到一个索引位置,并在该位置存储对应的值来实现高效的插入、查找和删除操作。 3. 列表(List):列表是一个有序的字符串集合,可以进行插入、删除和查找等操作。Redis中的列表底层实现使用了双向链表(Linked List),每个节点都包含一个指向前一个节点和后一个节点的指针。 4. 集合(Set):集合是一个无序且不重复的字符串集合。Redis中的集合底层实现使用了哈希表,每个元素被存储在哈希表的一个桶中,通过哈希函数来计算元素的索引位置。 5. 有序集合(Sorted Set):有序集合是一个有序的字符串集合,每个元素都有一个对应的分数,可以根据分数进行排序。Redis中的有序集合底层实现使用了跳跃表(Skip List)和哈希表。跳跃表通过多层索引来实现快速查找,而哈希表用于存储每个元素的分数和值。 这些底层数据结构的选择和实现方式使得Redis在插入、查找和删除等操作上具有高效性能和低复杂度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lolxxs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值