Redis(字典&hash表)

字典也可以称为Map、关联数组、映射、符号表。字典表在C语言中没有实现,所以Redis知己实现了字典。

在字典中一个key对应一个value。key是唯一的。这些关联的键和值称为键值对。

​ 字典的应用非常广泛,Redis数据库的底层实现就是字典,对数据库的增删改查都是使用的字典。

字典的结构

c 复制代码
typedef struct dict {
    //类型特定函数
    dictType *type;
    //私有数据
    void *privdata;
    //哈希表
    dictht ht[2];
    // rehash索引
    //当rehash不在进行时,值为-1
    in trehashidx; /* rehashing not in progress if rehashidx == -1 */
} dict;
  1. dictht ht2 :这是一个哈希表,长度为2。在没有rehash时只有第一个节点在使用,当rehash时会使用到哈希表的第二个节点。
  2. trehashidx:当rehash不在进行时,值为-1。后面会说到rehash。、
hash节点结构
c 复制代码
typedef struct dictEntry {
    //键
    void *key;
    //值
    union{
        void *val;
        uint64_tu64;
        int64_ts64;
    } v;
    //指向下个哈希表节点,形成链表
    struct dictEntry *next;
} dictEntry;

hash节点中存储的是key和value。假如存储的是String类型。则存储的数据如下 set s hello

同理以字典表为基础也能够实现集合、hash、列表、有序列表这些数据结构。我就不一一列举了。

Hash表

结构:

c 复制代码
typedef struct dictht {
    //哈希表数组
    dictEntry **table;
    //哈希表大小
    unsigned long size;
    //哈希表大小掩码,用于计算索引值
    //总是等于size-1
    unsigned long sizemask;
    //该哈希表已有节点的数量
    unsigned long used;
} dictht;
  1. dictEntry:hash节点 dictEntry
  2. size:hash数组长度。
  3. sizemask:计算索引值 = size -1
  4. used:该hash表已有的节点数量

示例结构:

Hash冲突

​ Hash表存储数据首先是要进行hash计算。计算规则是根据键的先计算一个hash值然后再根据hash表数组长度size和sizemask计算该键值对需要放到那个索引下面。

​ 如果两个键计算的索引位置时一样的则会用hash节点的next进行连接生成一个hash链表的方式。

示例:

rehash和渐进式rehash

字典表中的ht是长度为2的hash数组。上面的介绍都没有提及ht1的用处。ht1是为了在数据越来越多时进行rehash使用的,你可以理解为把hash表的数组增长然后再把原来的数据移动到增长后的数组上去。

步骤:

  1. 为字典的ht1哈希表分配空间,这个哈希表的空间大小取决于要执行的操作,以及ht0当前包含的键值对数量(也即是ht0.used属性的值):
    1. 如果执行的是扩展操作,那么ht1的大小为第一个大于等于ht0.used*2的2 n(2的n次方幂);
    2. 如果执行的是收缩操作,那么ht1的大小为第一个大于等于ht0.used的2 n。
  2. 将保存在ht0中的所有键值对rehash到ht1上面:rehash指的是重新计算键的哈希值和索引值,然后将键值对放置到ht1哈希表的指定位置上。
  3. 当ht0包含的所有键值对都迁移到了ht1之后(ht0变为空表),释放ht0,将ht1设置为ht0,并在ht1新创建一个空白哈希表,为下一次rehash做准备。

示例:


渐进式hash
  1. 为ht1分配空间,让字典同时持有ht0和ht1两个哈希表。
  2. 在字典中维持一个索引计数器变量rehashidx,并将它的值设置为0,表示rehash工作正式开始。
  3. 在rehash进行期间,每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定的操作以外,还会顺带将ht0哈希表在rehashidx索引上的所有键值对rehash到ht1,当rehash工作完成之后,程序将rehashidx属性的值增一。
  4. 随着字典操作的不断执行,最终在某个时间点上,ht0的所有键值对都会被rehash至ht1,这时程序将rehashidx属性的值设为-1,表示rehash操作已完成。渐进式rehash的好处在于它采取分而治之的方式,将rehash键值对所需的计算工作均摊到对字典的每个添加、删除、查找和更新操作上,从而避免了集中式rehash而带来的庞大计算量。
  • 具体的步骤讲解可以看《Redis的设计与实现》4.4rehash

式,将rehash键值对所需的计算工作均摊到对字典的每个添加、删除、查找和更新操作上,从而避免了集中式rehash而带来的庞大计算量。

  • 具体的步骤讲解可以看《Redis的设计与实现》4.4rehash
相关推荐
aqiu1111118 小时前
【C++算法打怪专栏】LeetCode 1. 两数之和(哈希表解法总结)
c++·算法·leetcode
楚识科技8 小时前
票据 OCR 识别技术深度解析:从算法原理到企业级落地实现
算法·ocr
longlongzihan8 小时前
LeetCode 25. K 个一组翻转链表 (迭代+分组反转法)
算法·leetcode·链表
xuxigifxfh8 小时前
牛客:BISHI8 大整数哈希
java·算法·哈希算法
涉密IT资质笔记8 小时前
涉密资质投入产出分析模型:时间成本、四块成本构成与 ROI 决策框架
java·开发语言·算法
木井巳8 小时前
【BFS 解决拓扑排序】火星词典
java·算法·leetcode·广度优先·宽度优先·图搜索算法
北风toto8 小时前
KMP 算法详解:从暴力匹配到 O(n+m) 的字符串搜索
java·开发语言·算法
林澈在路上8 小时前
哪个写歌软件可以用自己声音?MELO音乐详解
人工智能·python·算法
爱读书的小胖8 小时前
Codewars Sum of a Sequence Hard Core Version C 解题分析
c语言·数据结构·算法
不会就选b8 小时前
算法日常・每日刷题--<动态规划>24
算法·动态规划