Redis 字典结构细谈
Redis 字典底层基于哈希表实现。
一、哈希表结构
1、dictht:
typedef struct dictht {
dictEntry **table; //哈希表数组,存储具体的键值对元素,对象类型 dictEntry
unsigned long size; //哈希表容量
unsigned long sizemask; //哈希表大小掩码,计算索引使用
unsigned long used; //已使用容量
} dictht
2、示例数据:

二、哈希表节点
1、dictEntry:
typedef struct dictEntry {
void *key; //键值对 key
union{ //键值对 value 三种类型
void *val;
uint64_tu64;
int64_ts64;
} v;
struct dictEntry *next; //下一个节点指针
} dictEntry;
说明:next 为指向下一个节点的指针,是我们熟悉的链表节点结构,单向链表,用于处理键哈希冲突问题。
相同哈希值的键的键值对会以链表的形式存在同一位置。
2、示例数据:

三、Redis 字典
1、dict:
typedef struct dict{
dictType *type; //类型特定函数
void *privdata; //私有数据
dictht ht[2]; //哈希表数组,类型为dictht,ht[0]为实际存储数据使用,ht[1] 为rehash时使用
int rehashidx; //rehash进度标志,-1 代表当前不在 rehash
} dict
2、示例数据:

四、添加元素
向字典中添加元素主要涉及一下几步操作:
1、计算键值对键的哈希值
hash:dict->type->hashFunction(key)
使用dictType内部的哈希函数得到键哈希值
2、计算需要放入的位置索引
index:hash&dict->ht[0].sizemask
使用上一步计算得到的哈希值与哈希表的sizemask属性进行与操作得到需要放入的位置索引值
3、键冲突解决
没有完美的哈希函数,哈希冲突往往无法避免,当多个键被所引导同一个位置时,这种现象,我们称之为键冲突。
解决间冲突,Redis 采用链地址法,也即将冲突的键值对组成一条链条放到同一个哈希位置上。上面第二节我们介绍过 dictEntry的结构,其中包含一个指向另一个节点的指针next。
这里需要说明的一点是,冲突节点插入时,是插入到链表的头部,这样只需要执行操作一次操作即可,也即时间复杂度为O(1)。
如下图:(k2,v2)与(k1,v1)发生冲突,直接将(k2,v2)插入到链表头部:

五、rehash
rehash过程是在重新规划哈希表占用空间时发生的。
负载因子 load_factor:已保存节点数量(dict.ht[0].used)/ 哈希表容量(dict.ht[0].size)
负载因子用以表名当前哈希表的使用状态,它需要保持在一个合理的范围,以保障资源的最优利用。通常需要适时的对哈希表进行扩展或者收缩来对负载因子进行维护,而这个过程,我们称之为 rehash。
这里涉及到一个问题,就是什么时候需要进行伸缩维护?
1、扩展时机:
当前无bgsave及bgrewriteaop操作,load_factor >= 1
当前存在bgsave及bgrewriteaop操作,load_factor >= 5
Redis服务器通过fork子进程形式执行bgsave及bgrewriteaop操作,此时整个服务的资源耗费较大,为了避免可能发生的rehash带来额外的资源压力,此期间,服务器会调高触发执行扩展操作的负载因子界限。
2、收缩时机:
load_factor < 0.1
3、rehash 基本操作:
a) 为dict.ht[1]分配空间:
空间大小计算如下:
扩展:最小n满足2n >= dict.ht[0].used * 2
收缩:最小n满足2n >= dict.ht[0].used
如下图:ht[0].used = 3,假定无bg相关任务,则h[1]大小需要计算:2n >= 3 * 2 = 6
n = 3,ht[1].size = 23 = 8

b) rehash
对于dict.ht[0] 中的元素,依据dict.ht[1]特性(sizemask)重新计算索引值,并放置到dict.ht[1]中。

c) 当所有元素迁移完毕,释放dict.ht[0],并将dict.ht[1]设置为dict.ht[0],重新在dict.ht[1]上创建空的哈希表。

六、渐进式rehash
所谓渐进式,是针对大数据量字典数据。直接一次性的执行rehash会导致服务资源的集中占用,影响正常的服务响应。因此需要进行分而治之。
这里会用到上面我们介绍的dict字典结构中的 rehashidx属性,用以标识当前rehash进度。
首先将rehashidx置0,标示rehash开始,每次rehash一个元素,rehashidx值增加1,当最终所有元素rehash完成,将rehashidx置-1。
这里需要说明下rehash中对正常的服务请求的处理:
1、删除、查找、更新:
会涉及到两个哈希表(ht[0]、ht[1])操作,如查找元素,首先尝试在ht[0]上查找,找不到,则继续在h[1]上查找。
2、添加
添加元素只会在h[1]上操作,h[0]上只减不增。
Redis 字典结构细谈的更多相关文章
- 《闲扯Redis七》Redis字典结构的底层实现
一.前言 上节<闲扯Redis六>Redis五种数据类型之Hash型 中说到 Hash(哈希对象)的底层实现有: 1.ziplist 编码的哈希对象使用压缩列表作为底层实现 2.hasht ...
- Redis核心原理与实践--散列类型与字典结构实现原理
Redis散列类型可以存储一组无序的键值对,它特别适用于存储一个对象数据. > HSET fruit name apple price 7.6 origin china 3 > HGET ...
- redis中的字典结构是怎样的?
点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. 基础概念 redis支持的5种数据类型中,有hash类型,hash类型的 ...
- 【Redis 系列】redis 学习十六,redis 字典(map) 及其核心编码结构
redis 是使用 C 语言编写的,但是 C 语言是没有字典这个数据结构的,因此 C 语言自己使用结构体来自定义一个字典结构 typedef struct redisDb src\server.h 中 ...
- REDIS 字典数据结构
对于REDIS来讲 其实就是一个字典结构,key ---->value 就是一个典型的字典结构 [当然 对于vaule来讲的话,有不同的内存组织结构 这是后话] 试想一个这样的存储场景: ...
- redis 字典
redis 字典 前言 借鉴了 黄健宏 的 <<Redis 设计与实现>> 一书, 对 redis 源码进行学习 欢迎大家给予意见, 互相沟通学习 概述 字典是一种用于存储键值 ...
- Redis 字典的实现
[Redis 字典的实现] 注意 dict 类型使用了两个指针,分别指向两个哈希表. 其中, 0 号哈希表(ht[0])是字典主要使用的哈希表, 而 1 号哈希表(ht[1])则只有在程序对 0 号哈 ...
- [算法]从Trie树(字典树)谈到后缀树
我是好文章的搬运工,原文来自博客园,博主July_,地址:http://www.cnblogs.com/v-July-v/archive/2011/10/22/2316412.html 从Trie树( ...
- 阿里面试官:HashMap 熟悉吧?好的,那就来聊聊 Redis 字典吧!
最近,小黑哥的一个朋友出去面试,回来跟小黑哥抱怨,面试官不按套路出牌,直接打乱了他的节奏. 事情是这样的,前面面试问了几个 Java 的相关问题,我朋友回答还不错,接下来面试官就问了一句:看来 Jav ...
随机推荐
- SQL用于更新ID为顺序ID(用于删除了几条数据导致id不连续)
UPDATE tab1022zhm SET ID =FROM (SELECT ROW_NUMBER() OVER (ORDER BY id) ROW,comName FROM dbo.tab1022z ...
- Scrapy框架的架构原理解析
爬虫框架--Scrapy 如果你对爬虫的基础知识有了一定了解的话,那么是时候该了解一下爬虫框架了.那么为什么要使用爬虫框架? 学习框架的根本是学习一种编程思想,而不应该仅仅局限于是如何使用它.从了解到 ...
- robotframework安装与运行(ride.py1.7.4.2命令报错)(win10+python3.8.1)
首先,robotframework必须在python的环境下运行 所以你的电脑里必须有python3.x的环境先. 上古时期的robotframework只支持python2,不过python2都已经 ...
- Linux rndis_host 驱动的一个BUG与解决方案
关键字 rndis_host, linux, kernel, modem 综述 rndis 是微软定义的一套通讯方案.类似的协议还有 qmi/mbim/ecm/ncm. rndis 协议足够简单,可靠 ...
- Kubernetes调度流程与安全(七)
一.Kubernetes中的调度流程 1,介绍 Scheduler 是 k8s 中的调度器,主要的任务是把定义的 Pod 分配到集群的节点上.Scheduler 是作为一个单独的程序运行的,启动之后会 ...
- 我的Python自学之路-002 字典的知识
'''字典是python中唯一的验证类型,采用键值对(key-value)的形式存储数据.python对key进行哈希函数运算.根据计算的结果决定value的存储地址.所以字典是无序存储的.且key必 ...
- Shell编程(4)
shell函数 shell中允许将一组命令集合或语句形成一段可用代码,这些代码块称为shell函数.给这段代码起个名字称为函数名,后续可以直接调用该段代码. 格式 func() { #指定函数名 co ...
- SQLSERVER如何在子查询中使用ORDER BY
今天在使用公司的一个pager接口的时候,需要传递一个查询的SQL语句,因为我希望他能够在pager对他查询出来的结果排序之前自己先进行排序, 于是在这个SQL中添加了ORDER BY,但是得到的结果 ...
- Python练习题 049:Project Euler 022:姓名分值
本题来自 Project Euler 第22题:https://projecteuler.net/problem=22 ''' Project Euler: Problem 22: Names sco ...
- 音频数据增强及python实现
博客作者:凌逆战 博客地址:https://www.cnblogs.com/LXP-Never/p/13404523.html 音频时域波形具有以下特征:音调,响度,质量.我们在进行数据增强时,最好只 ...