字典(dict)

字典又称为符号表或者关联数组、或映射(map),是一种用于保存键值对(key-value)的抽象数据结构

字典中的每个key都是唯一的,通过key对值来进行查找或修改,时间复杂度为 O(1)

redis的底层数据结构字典又使用了哈希表,一个哈希表包含多个哈希表节点,每个哈希表节点保存了字典中的一个键值对

1 字典结构

typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
int rehashidx;
} dict;

其中:

type:类型特定函数,保存了一系列操作dict的函数, redis会为用途不同的字典设置不同的类型特定函数

privdata:私有数据,需要传给type属性中定义的类型特定函数的可选参数

dictht:哈希表,包含两个元素,每个元素都是一个哈希表,一般情况下,字典只使用 ht[0]哈希表,ht[1]哈希表只会在对 ht[0] 哈希表进行 rehash 时使用

rehashidx:rehash索引,不在进行rehash时,值为 -1

2 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 (*valDestructor)(void *privdata, void *obj);
} dictType;

其中:

hashFunction:计算哈希值的函数

keyDup:复制键的函数

valDup:复制值的函数

keyCompare:对比键的函数

keyDestructor:销毁键的函数

valDestructor:销毁值的函数

3 dictht(哈希表)结构

typedef struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
} dictht;

其中:

table:哈希表数组,每一个哈希表数组元素都保存了一对键值对

size:哈希表的大小

sizemask:哈希表掩码,值等于 size-1,用于通过hash算法计算索引值

used:哈希表已使用节点的数量

4 dictEntry(哈希表节点)结构

typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
} v;
struct dictEntry *next;
} dictEntry;

其中:

key:dict中的key

v:dict的value,可以是一个指针,也可以是uint64_t 整数,也可以是int64_t整数

next:指向下一个哈希表节点的指针,当有hash冲突时,解决hash冲突使用,redis使用链地址法解决哈希冲突

5 字典示意图

存有4个key-value对且没有进行rehash的字典结构如下:

type=REDIS_SET 或 REDIS_HASH

6 hash算法

dict要添加新的key-value对时, redis使用hash算法计算索引值, 根据索引值将key-value对放到哈希表数组的对应位置

redis中计算索引值步骤如下:

1) 计算key的哈希值

使用dict结构的type属性中定义的类型特定函数,计算键(key)的哈希值

redis 使用 MurmurHash2 算法来计算键的哈希值

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

2) 计算索引值

使用哈希表的sizemake属性和哈希值,计算出索引值,根据情况不同:ht[x]可以是 ht[0]或者 ht[1]

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

7 hash冲突

哈希表会存在哈希冲突,解决哈希冲突的方法有:开放地址法和链地址法

redis采用的是链地址法,通过next指针可以将多个哈希值相同的键值对连接在一起,用来解决哈希冲突

rehash会增加现有的哈希桶数量,让逐渐增大的entry元素能在更多的桶之间分散保存,减少单个桶中的元素数量,从而减少单个桶中的冲突

8 扩容和收缩(rehash)

当哈希表保存的key-value对太多或太少时,就要通过rehash(重新散列)对哈希表进行扩展或者收缩

dict中存有的两张哈希表:

ht[0]哈希表表示字典正在使用的哈希表,key-value对存在ht[0]中

ht[1]哈希表表示rehash时使用的哈希表,没有申请结点内存空间,只有表结构,不会占用很大的内存空间

步骤如下:

1) 为ht[1]重新分配空间,分配的空间大小取决于要执行的操作,以及ht[0]当前包含的key-value对数量(即ht[0].used的值)

  a 如果执行的是扩展操作,扩展为原哈希表已使用的空间的2倍

  b 如果执行的是收缩操作,收缩为原哈希表已使用空间的一半

2) 使用hash算法重新计算键的哈希值和索引,然后将key-value对存放到ht[1]的对应位置上
3) 当ht[0]包含的所有key-value对都迁移到ht[1]之后(ht[0]变为空),释放ht[0],将ht[1]设置为ht[0],rehashidx设置为 -1

9 渐近式rehash

扩容和收缩操作不是一次性、集中式完成的,而是分多次、渐进式完成的

如果哈希表里保存的key-vlaue对的数量在百万级别,那么一次性将所有key-vlaue对rehash,可能会造成redis在一段时间内不能进行别的操作,所以redis采用渐进式rehash,分多次、渐进式完成地将ht[0]里面的key-vlaue对慢慢地rehash到ht[1]

dict中索引计数器变量rehashidx=0表示开始rehash,每次处理请求时将ht[0]索引位置(rehashidx=上一个rehashidx+1)的所有key-value对拷贝到ht[1],完成拷贝时,rehashidx+1;

渐进式rehash期间,字典的删除、查找、更新等可能会在两个哈希表上进行(不是同时进行),第一个哈希表没有找到,就会去第二个哈希表上进行查找;但是插入key-value对一定是在新的哈希表上进行

10 扩容和收缩的条件

负载因子 = 哈希表已保存节点数量 / 哈希表大小

1) 扩容操作

a 服务器目前没有在执行bgsave命令或者bgrewriteaof命令并且哈希表的负载因子大于等于1

b 服务器目前正在执行bgsave命令或者bgrewriteaof命令并且哈希表的负载因子大于等于5

2) 收缩操作

a 哈希表的负载因子小于 0.1 ,程序自动开始对哈希表执行收缩操作

redis底层数据结构之字典(dict)的更多相关文章

  1. Redis 底层数据结构之字典

    文章参考 <Redis 设计与实现>黄建宏 字典 在字典中,每个键都是独一无二的,程序可以在字典中根据键查找与之相关联的值,或者通过键来更新和删除值. 字典在 Redis 中的应用相当广泛 ...

  2. Redis 底层数据结构介绍

    Redis 底层数据结构 版本:2.9 支持的数据类型: 字符串 散列 列表 集合 有序集合 字符串 Redis 利用原生的 c 字符串进行了一次封装.封装的字符串叫做简单动态字符串:SDS(simp ...

  3. Redis底层数据结构详解

    上一篇说了Redis有五种数据类型,今天就来聊一下Redis底层的数据结构是什么样的.是这一周看了<redis设计与实现>一书,现来总结一下.(看书总是非常烦躁的!) Redis是由C语言 ...

  4. 【redis】redis底层数据结构原理--简单动态字符串 链表 字典 跳跃表 整数集合 压缩列表等

    redis有五种数据类型string.list.hash.set.zset(字符串.哈希.列表.集合.有序集合)并且自实现了简单动态字符串.双端链表.字典.压缩列表.整数集合.跳跃表等数据结构.red ...

  5. Redis学习笔记(二)redis 底层数据结构

    在上一节提到的图中,我们知道,可以通过 redisObject 对象的 type 和 encoding 属性.可以决定Redis 主要的底层数据结构:SDS.QuickList.ZipList.Has ...

  6. 探索Redis设计与实现2:Redis内部数据结构详解——dict

    本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...

  7. Redis底层数据结构实现

    REDIS  较宽泛的支持5种数据结构  分别为 字符串 列表 集合 散列 有序集合 关于这几种数据结构的使用 相信网上有很多资料,查看官网API 也很详细了  读者可以自己随意翻阅 很方便 . 接下 ...

  8. Python学习笔记(5)--数据结构之字典dict

    字典(dict) 定义:键值对集合 初始化:{}, {'1' : 'abc', '2' : 'def'} 1.增加:单个数据直接赋值  update(dict2) ---把dict2的元素加入到dic ...

  9. redis底层数据结构--简单动态字符串 链表 字典 跳跃表 整数集合 压缩列表

    1.动态字符串 redis中使用c语言的字符床存储字面量,默认字符串存储采用自己构建的简单动态字符串SDS(symple dynamic string) redis包含字符串的键值对都是用SDS实现的 ...

  10. Redis 的底层数据结构(字典)

    字典相对于数组,链表来说,是一种较高层次的数据结构,像我们的汉语字典一样,可以通过拼音或偏旁唯一确定一个汉字,在程序里我们管每一个映射关系叫做一个键值对,很多个键值对放在一起就构成了我们的字典结构. ...

随机推荐

  1. DVWA靶场实战(七)——SQL Injection

    DVWA靶场实战(七) 七.SQL Injection: 1.漏洞原理: SQL Inject中文叫做SQL注入,是发生在web端的安全漏洞,主要是实现非法操作,例如欺骗服务器执行非法查询,他的危害在 ...

  2. 【Machine Teaching】An Overview of Machine Teaching

    Machine Teaching 1 Introduction 1️⃣ 什么是 Machine Teaching? searching the optimal (usually minimal) te ...

  3. P1605迷宫——题解

    展开 题目背景 给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过.给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案.在迷宫中移动有上下左右四种方式,每次 ...

  4. 近邻取样插值方法缩放BGRA图片数据

    近邻取样插值原理: 对于缩放后图片中的某点 (Dx, Dy) 对应于原图片中的点 (Sx, Sy),它们之间存在如下的比例关系: (Sx-0)/(SW-0)=(Dx-0)/(DW-0) (Sy-0)/ ...

  5. flutter 2.x运行flutter run 报错Cannot run with sound null safety, because the following dependenciesdon'

    flutter 2.x运行flutter run 报错Cannot run with sound null safety, because the following dependenciesdon' ...

  6. 轻松理解Promise.all 、Promise.then、Promise.race有什么区别以及使用方法

    简单来说呢,Promse.all一般应用于某个场景需要多个接口数据合并起来才能实现 有个极大地好处我必须说一下,请求顺序和获取数据顺序是一样的哟,大可放心使用~~ const success1 = n ...

  7. Python从零到壹丨图像增强及运算:图像掩膜直方图和HS直方图

    摘要:本章主要讲解图像直方图相关知识点,包括掩膜直方图和HS直方图,并通过直方图判断黑夜与白天,通过案例分享直方图的实际应用. 本文分享自华为云社区<[Python从零到壹] 五十二.图像增强及 ...

  8. 问题记录:VMware vSphere vCenter 7.0 上传文件失败

    问题记录:VMware vSphere vCenter 7.0 上传文件失败 环境说明: VC版本:VMware vSphere vCenter 7.0 ESXi版本:VMware vSphere E ...

  9. MySQL 合并查询join 查询出的不同列合并到一个表中

    为了求解问题时思路清晰,建议先分列查询,再将列合并到一个表中,这样相当于将复杂问题拆解为简单问题,一一解决.优点是避免所有问题混在一起,代码逻辑清晰,可迁移性强,下次遇到类似的查询问题能快速求解,缺点 ...

  10. Djanngo-bbs项目

    1.项目开发基本流程 1.需求分析 2.架构设计 3.分组开发 4.提交测试 5.交付上线 2.项目流程 仿造博客园项目(核心:文章的增删改查) 1.表分析: 1.1用户表 1.2个人站点表 1.3文 ...