Redis中的字典

原文链接:https://www.changxuan.top/?p=1122

简介

字典是一种在 Redis 中高频使用的用于保存键值对的抽象数据结构,在 Java 中常用的有 HasmMap 等。

由于字典中键的唯一性,所以在 Redis 中得到了广泛的应用。

实现

Redis 中的字典是基于哈希表 (dictht, dict hash table)实现的,哈希表中的每个节点保存一个键值对。哈希表的结构体定义如下:

typedef struct dictht {
  // 哈希表数组
  dictEntry **table;
  // 哈希表大小
  unsigned long size;
  // 哈希表大小掩码,用于计算索引值 size - 1,用来计算键值对放在哪个索引上
  unsigned long sizemask;
  // 哈希表已有节点的数量
  unsigned long used;
} dictht;

哈希表节点 dictEntry 的结构则如下所示:

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

dictEntry 中的值有些特别,它表示其值有可能是一个指针或者是一个 uint64_t 整数,或者是一个 int64_t 整数。

因为存在 next 属性,很显然它是使用链地址法解决的哈希键冲突。

接下来我们看一下字典(dict)的定义:

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

属性 type 是一个指向 dictType 结构体的指针,每个 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 (*valDestructor)(void *privdata, void *obj);
} dictType;

ht 数组表示存储两个哈希表,平常情况下只使用 ht[0] ,只有在 rehash 时才会使用到 h[1]trehashids

字典的结构就是,一个字典中有两个哈希表,平时只用一个哈希表。另一个哈希表在 rehash 的时候使用。每个哈希表中存在一个节点数组,节点则用于存放键值对。

新增键值对

新增键值对就意味着需要计算键的哈希值,从而得出索引值。根据索引值将键值对的哈希节点放到哈希表的指定位置上。计算哈希值使用的是字典结构体中的 type 中的函数,即 hash = dict->type->hashFunction(key) 。计算索引值则是 index = hash & dict->ht[x].sizemask ,x 取决于当前使用的是ht[1]还是ht[2]。

不过,总会有不同的键对应相同的索引值,产生冲突。Redis 中使用了常用的“链地址法”来解决这个问题,当出现冲突时就把新节点放到表头的位置。

Rehash

随着字典中键值对数量的不断变化,为了保证哈希表的空间利用率以及效率,在哈希表过大或者过小是要对哈希表大小进行调整。如果过小,则会不断发生键冲突导致效率低下,如果过大则会浪费存储空间。所以,经过不断调整可以使其维持在一个合理的范围。

步骤

  1. ht[1] 分配空间,大小取决于是扩大哈希表还是缩小哈希表。如果扩大,其大小为第一个大于等于 ht[0].used * 2 且同时为2的n次方幂 的值。如果缩小,其大小为第一个大于等于 ht[0].used 其同时为 2的n次方幂 的值。

  2. 将保存在 ht[0] 中所有的键值对重新计算哈希值和索引值后,存放在 ht[1] 中。

  3. 当迁移完所有的键值之后,释放原 ht[0] 的空间,将原 h[1] 改为 h0, 并在 ht[1] 新创建一个空白哈希表。

那么何时扩展哈希表大小呢? 一是当没有在执行 BGSAVE 或者 BGREWRITEAOF 命令时,并且哈希表的负载因子大于等于1时。 二是当在执行这俩命令,但是负载因子大于等于5时(节约内存,上述两命令消耗内存)。

负载因子计算公式为:负载因子 = 哈希表保存节点数量/哈希表大小

那么何时缩小哈希表大小呢? 当哈希表负载因子小于 0.1 时则会进行缩小。

渐进式 Rehash

其实对于上述步骤 2 ,普通人觉得这不就是把键值对重新分配一下吗?但是如果此时存在百万、千万甚至亿级的键值对时,恐怕就是不是一眨眼的功夫就可以完成的了。如果非得一次性完成,那么可能会导致服务器的不可用。所以为了解决这个问题,Redis 采用了慢慢来的办法渐进式 Rehash

其主要步骤与前面的有些相似,只不过在渐进式Rehash中使用到了 dict->trehashids 值来记录当前rehash到了哪个索引。在 Rehash 期间,可以对字典正常进行增加、删除、查找和更新。然后同时也会将 trehashids 上记录的索引值上的节点迁移到 h[1] 上。并且所有的新增节点都会放到 h[1]中,这样就会导致 h[0] 中的节点越来越少,最终完成 rehash。其它的操作则会在两个表上进行。

已标记关键词 清除标记
相关推荐
<p> <span style="background-color:#FFFFFF;color:#535353;font-size:medium;"><strong>简介:</strong>本门课程主要讲解的是缓存中间件Redis常见且典型的</span><span style="background-color:#FFFFFF;color:#535353;font-size:medium;">数据结构、</span><span style="background-color:#FFFFFF;color:#535353;font-size:medium;">相关的核心技术栈及其典型</span><span style="background-color:#FFFFFF;color:#535353;font-size:medium;">的</span><span style="background-color:#FFFFFF;color:#535353;font-size:medium;"></span><span style="background-color:#FFFFFF;color:#535353;font-size:medium;">应用场景的实战,其中涉及到的相关</span><span style="background-color:#FFFFFF;color:#535353;font-size:medium;">代码实战主要是基于SpringBoot2.x搭建的项目进行实战实现的!</span> </p> <p> <span style="background-color:#FFFFFF;color:#535353;font-size:medium;">其中的数据结构就包括:字符串String/列表List/集合Set/有序集合SortedSet/哈希Hash以及Key的过期失效等等,在课程的最后还介绍了“点赞系统中点赞功能模块的设计与实现”,可以说是拒绝纸上谈兵、注重实战并<span style="color:#535353;font-size:medium;background-color:#FFFFFF;">学以致用,课程的大纲如下所示:</span></span> </p> <p> <span style="background-color:#FFFFFF;color:#535353;font-size:medium;"><img src="https://img-bss.csdn.net/202003110913036582.jpg" alt="" /><br /> </span> </p> <p> <span style="background-color:#FFFFFF;color:#535353;font-size:medium;">下面贴一贴本课程重点介绍的数据结构及其典型的应用场景吧(完整的请参考课程的目录),见下面的大图:</span> </p> <p> <span style="background-color:#FFFFFF;color:#535353;font-size:medium;"><img src="https://img-bss.csdn.net/202003110915377703.png" alt="" /><br /> </span> </p> <p> <br /> </p> <p> <span style="background-color:#FFFFFF;color:#535353;font-size:medium;"><img src="https://img-bss.csdn.net/202003110916106961.png" alt="" /><br /> </span> </p> <p> <span style="background-color:#FFFFFF;color:#535353;font-size:medium;"><br /> </span> </p> <p> <span style="background-color:#FFFFFF;color:#535353;font-size:medium;"><img src="https://img-bss.csdn.net/202003110916349998.png" alt="" /><br /> </span> </p> <p> <span style="background-color:#FFFFFF;color:#535353;font-size:medium;"><br /> </span> </p> <p> <span><span style="background-color:#FFFFFF;">其中,“点赞功能模块的设计与实战实现”可以说是本门课程的核心重点与高潮!如下几张图所示即为点赞功能模块所拆分出来的诸多详细的功能流程图:</span></span> </p> <p> <span><span style="background-color:#FFFFFF;"><img src="https://img-bss.csdn.net/202003110917542738.png" alt="" /><br /> </span></span> </p> <p> <span><span style="background-color:#FFFFFF;"><img src="https://img-bss.csdn.net/202003110918035932.png" alt="" /><br /> </span></span> </p> <p> <span><span style="background-color:#FFFFFF;"><img src="https://img-bss.csdn.net/202003110918101389.png" alt="" /><br /> </span></span> </p> <p> <span><span style="background-color:#FFFFFF;"><img src="https://img-bss.csdn.net/202003110918186610.png" alt="" /><br /> </span></span> </p> <p> <br /> </p> <p> <span><span style="background-color:#FFFFFF;">掌握本门课程相关的技术要点之后,debug相信各位小伙伴在面试以及实际项目实战开发过程中将能带来大大的帮助....</span></span> </p> <p> <span style="background-color:#FFFFFF;color:#535353;font-size:medium;">其他相关的核心功能在这里就不一 一详细介绍了....还等什么呢,此时不学习redis,更待何时呀!!!</span> </p>
<p> <span style="font-size:16px;">概要介绍</span><span style="font-size:16px;">:历经半个多月的时间,Debug呕心沥血、亲自录制的 “缓存中间件Redis技术入门与典型应用场景实战(SpringBoot2.x)”的新课终于完成了,顾名思义,这是一门关于目前相当流行的分布式缓存中间件Redis相关技术栈的介绍与实战,目的在于带领各位小伙伴一起学习、攻克Redis,更好地巩固自己的核心竞争力,跳槽涨薪 自然不在话下!</span> </p> <p> <span style="font-size:16px;"><img src="https://img-bss.csdn.net/201911040416386002.png" alt="" /><br /> </span> </p> <p> <span style="font-size:16px;">值得介绍的是,本课程在技术层面涵盖了中间件<span>Redis</span>的相关技术栈,比如数据结构<span>String</span>、<span>List</span>、<span>Set</span>、<span>SortedSet</span>以及<span>Hash</span>等等,除此之外,在设计并实战“抢红包系统”以及实战各种数据结构对应的应用场景期间,也使用了微服务、分布式相关的技术。包括<span>SpringBoot2.x</span>、<span>Mybatis</span>、热部署工具、二倍均值法、多线程并发编程、发送邮件等等。<br /> </span> </p> <p> <span style="font-size:16px;"><img src="https://img-bss.csdn.net/201911040416139857.png" alt="" /><br /> </span> </p> <p> <span style="font-size:16px;"><img src="https://img-bss.csdn.net/201911040512437801.png" alt="" /><br /> </span> </p> <p> <span style="font-size:16px;"><img src="https://img-bss.csdn.net/201911040512574137.png" alt="" /><br /> </span> </p> <p> <span style="font-size:16px;"><img src="https://img-bss.csdn.net/201911040513156621.png" alt="" /><br /> </span> </p> <span></span>
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页