Redis源码分析(三)——Redis数据结构-字典

1. 数据结构

1.1 哈希表

typedef struct dictht{
  dictEntry **table;
  unsigned long size;
  unsigned long sizemask;
  unsigned long used;
} dictht;
  • table:存储节点的数组
  • size:table数组的长度
  • sizemask:size-1,用于在添加节点时计算节点在table中的位置
  • used:节点数量

1.2 哈希表节点

typedef struct dictEntry{
  void *key
  union {
    void *val;
    unit64_t u64;
    int64_t s64;
  }v;
  struct dictEntry *next
} dictEntry;
  • key:节点的key
  • union:节点的value(可以是指针、unit64_t整数、int64_t整数)
  • next:下一个节点的地址

1.3 字典

typedef struct dict {
  dictType *type
  void *privdata
  dictht ht[2]
} dict;
  • type:操作哈希表的各种函数
  • privdata:上述函数所需的入参
  • ht[2]:存储两个哈希表,一个正常使用,另一个在rehash时使用。

2. 哈希算法

  1. 计算哈希值 hash = dict->type->hashFunction(key);
  2. 计算在table数组的位置 index = hash & dict->ht[0].sizemask;
  3. 插入节点 创建新节点,并将其插入到table[index]的第一位。

3. rehash

3.1 何时进行rehash?

当加载因子(load factor)大于1或小于0.1时就要进行rehash。 加载因子计算公式:

load_factor = ht[0].used / ht[0].size

3.2 新哈希表大小的计算公式

当需要进行扩容/缩容的时候,究竟创建多大的哈希表呢?这取决于如下公式:

  • 若要进行扩容,则新的哈希表的大小=第一个大于等于h[0].used*2的2的n次方。
  • 若要进行缩容,则新的哈希表的大小=第一个大于等于h[0].used的2的n次方。

3.3 rehash过程

  1. 创建一个新的哈希表h[1],大小由上述公式计算得出;
  2. 将字典的rehashidx值从-1改为0;
  3. 依次遍历ht[0]上的所有节点,依次转移到ht[1]上去;
  4. 释放ht[0]的内存空间;

3.4 渐进式rehash

  • rehash过程中需要将所有节点迁移到新的哈希表中,如果节点个数很多的情况下,迁移的过程将非常漫长,那么程序将处于停止等待状态。所以事实上,Redis的rehash过程是分多次、分布完成的。
  • 在rehash过程中,每次对哈希表进行增删改查外,还要将ht[0][rehashidx]上的所有节点迁移到ht[1]中,并将rehashidx+1。从而几次操作后,ht[0]上的所有节点均被迁移至ht[1]中,rehash过程完成。
  • 在rehash过程中,对哈希表的添加操作均在ht[1]上完成,ht[0]只减不增。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏开发与安全

从零开始学C++之虚继承和虚函数对C++对象内存模型造成的影响(类/对象的大小)

首先重新回顾一下关于类/对象大小的计算原则: 类大小计算遵循结构体对齐原则 第一个数据成员放在offset为0的位置 其它成员对齐至min(sizeof(me...

2150
来自专栏人工智能头条

TensorFlow架构与设计:变量初始化

924
来自专栏Java技术

8 张图理解 Java

一图胜千言,下面图解均来自Program Creek 网站的Java教程,目前它们拥有最多的票选。如果图解没有阐明问题,那么你可以借助它的标题来一窥究竟。

621
来自专栏用户画像

7.7.4 置换选择排序(生成初始归并段)

7.7.3讨论了如何使用m路归并来减少磁盘访问次数。从第7.7.2的讨论可知,减少初始归并段个数r也可以减少归并趟数S。若总的记录个数为n,每个归并段的长度为L...

1182
来自专栏用户2442861的专栏

限制QLineEdit的数值输入范围

QLineEdit *lineEdit = new QLineEdit(this);

5991
来自专栏Python小屋

针对递归函数的优化与Python修饰器实现

我们围绕一个数学问题来说明本文的思想,组合数C(n,i),也就是从n个元素中任选i个,共有多少种选法。当然,这个问题有很多种求解方法,例如【最快的组合数算法之P...

4049
来自专栏夏时

PHP 常用函数大全

3972
来自专栏程序员互动联盟

【答疑释惑】C语言里面结构体大小统计方法

之前说过一个关于结构体在内存中所占字节数的问题,我们知道结构体长度的计算并不是所有成员长度的相加,而是因为编译器优化会对其进行对齐,这样会优化访问速度等。 那...

3007
来自专栏生信宝典

Python学习教程(二)

输入输出 交互式输入输出 在很多时候,你会想要让你的程序与用户(可能是你自己)交互。你会从用户那里得到输入,然后打印一些结果。我们可以分别使用raw_input...

2598
来自专栏mathor

LeetCode329. 矩阵中的最长递增路径

 dfs,主函数中枚举起点,然后dfs函数中枚举四个方向进行移动,但是光dfs还不够,因为我们发现存在很多冗余,所以这是一道dfs+dp的问题,resul...

1051

扫码关注云+社区