redis中的字典结构是怎样的?
点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人。
文章不定期同步公众号,还有各种一线大厂面试原题、我的学习系列笔记。
基础概念
redis支持的5种数据类型中,有hash类型,hash类型的底层采用字典结构(多对key-value)实现,而字典结构的代码实现=hashTable=用到了hash表
字典结构的实现
字典结构由三种结构组合而成:字典结构=dict+dictht+dictEntry,关系如下:

代码实现:
typedef struct dict {
dictType *type; //dictType也是一种数据结构,dictType结构中包含了一些函数,这些函数用来计算key的哈希值,进而用这个哈希值计算key在dictEntry型table数组中的下标
void *privdata; //私有数据,保存着dictType结构中函数的参数
dictht ht[2]; //两张哈希表:一张用来正常存储节点,一张用来在rehash时临时存储节点
long rehashidx; //rehash的标记:默认-1,当table数组中已有元素个数增加/减少到一定量时,整个字典结构将进行rehash给每个table元素重新分配位置,rehashidx代表rehash过程的进度,rehashidx==-1代表字典没有在进行rehash,rehashidx>-1代表该字典结构正在对进行rehash
} dict;
typedef struct dictht { //哈希表
dictEntry **table; //存放一个数组的地址,每个数组元素存放的是哈希表节点dictEntry的地址
unsigned long size; //哈希表的table数组长度小,初始化大小为4
unsigned long sizemask; //哈希表掩码sizemask的值总是等于(size-1),用于计算每个key在table中的下标位置=hash(key)&sizemask
unsigned long used; //记录哈希表的table中已有的节点数量(节点=dictEntry=键值对)。
} dictht;
typedef struct dictEntry {
void *key;//键
union{ //值
void *val;//值可以是指针
uint64_tu64;//值可以是无符号整数
int64_ts64;//值可以是带符号整数
} v;
struct dicEntry *next;//指向下个dictEntry节点:redis的字典结构采用链表法解决hash冲突,当table数组某个位置处已有元素时,该位置采用头插法形成链表解决hash冲突
} dictEntry;
用key计算(key-value)在table中的位置下标:
//1、先计算key的hash值:使用字典中计算key哈希值的函数
hash = dict结构->dictType结构的函数hashFunction(key)
//2、根据hash值与哈希表掩码sizemask进行“与运算”得到下标,x=(0或1)=两张哈希表中用来正常存储节点的那张哈希表的下标
index = hash & dict->ht[x].sizemask;
字典中的负载因子及rehash
先来看看几个重要概念:
redis字典中哈希表的rehash=扩展或收缩哈希表中的table数组长度;
负载因子=dict结构的ht[0].used/dict结构的ht[0].size=哈希表的table中已有的节点数量/哈希表的table数组长度;
bgsave操作:将redis内存中的数据以rdb的形式持久化到磁盘;
bgrewriteaof操作:将redis内存中的数据以aof的形式持久化到磁盘中,持久化成功后旧的aof文件会被替换(redis2.4之后aof由redis自动触发,而bgrewriteaof需要手动地触发)
redis中的哈希表什么时候进行扩展操作?
两种情况:
当没有bgsave操作 && 没有bgrewriteaof操作 && 负载因子>=1时;
当(正在bgsave操作 || 正在bgrewriteaof操作) && 负载因子>=5时;
负载因子为什么会>=1?因为当hash冲突时,新节点会以头插法的形式插入哈希表的table数组某个位置中形成链表,就会使table中节点的总数量>table数组长度,进而负载因子>=1
redis中的哈希表什么时候进行扩展操作?
当负载因子<=0.1时(如table数组长度经过多次扩展变为了16,某时刻table只有1个元素,1/16=0.0625<0.1,则哈希表进行rehash收缩table长度)
redis中的哈希表进行扩展或缩收缩的过程?
扩展:dict数据结构中有两张哈希表ht[2],ht[0]拿来正常地装redis数据(key-value),ht[1]用来进行rehash扩展时存放ht[0]的元素,直到ht[0]中所有元素被重新计算下标存放到ht[1]中的table数组为止,ht[1]的大小=第一个大于等于ht[0].used的2^n
收缩:同"扩展"操作一样,ht[1]拿来装在收缩过程中ht[0]的元素,ht[1]的大小=第一个大于等于ht[0].used的2^n(公式与扩展一样)
特点:
- 扩展或收缩完成后,释放哈希表ht[0],并把哈希表ht[1]置为ht[0],然后再重新分配一个新的空白ht[1]作为下次rehash使用
- 在rehash(扩展或收缩)过程中有两张哈希表,并且字典会同时使用两张哈希表:查找、删除、更新会同时操作两张哈希表(先在ht[0]中找,找不到再去ht[1]中找),'插入'则只操作ht[1]
- rehash过程是渐进式的,它采取分而治之的方法,以扩展为例,一开始先将rehashidx值由-1置为0代表rehash工作开始:
此时rehashidx为0,第一次对redis字典进行【添加、删除、查找或者更新】操作,则对ht[0]中table[rehashidx]=table[0]的数据重新计算下标放到ht[1]的table数组中,然后rehashidx自增为1;
此时rehashidx为1,第二次对redis字典进行【添加、删除、查找或者更新】操作,则对ht[0]中table[rehashidx]=table[1]的数据重新计算下标放到ht[1]的table数组中,然后rehashidx自增为1
......以此类推,直到ht[0]中的所有table元素都被重新计算下标rehash到ht[1]中为止,最后把rehashidx置为-1,此时rehash完成;
渐进式rehash可以把这个过程中的计算压力分摊到每次对字典进行【添加、删除、查找或者更新】操作的时候,避免在某一时刻对整个hash类型数据(redis的5中数据类型之一)进行庞大的rehash计算,进而避免了redis阻塞,唯一不好的地方就是在rehash时同时使用两个哈希表,导致redis内部使用量暴增
OK,如果文章哪里有错误或不足,欢迎各位留言。
创作不易,各位的「三连」是二少创作的最大动力!我们下期见!
redis中的字典结构是怎样的?的更多相关文章
- 快速整明白Redis中的字典到底是个啥
字典简介 字典是一种用于保存键值对的数据结构,可以通过键值对中的键快速地查找到对应的值.在Redis所使用的C语言中,并没有内置字典,所以Redis自己实现了字典. 整个Redis数据库的所有的键和值 ...
- Redis数据结构详解(2)-redis中的字典dict
前提知识 字典,又被称为符号表(symbol table)或映射(map),其实简单地可以理解为键值对key-value. 比如Java的常见集合类HashMap,就是用来存储键值对的. 字典中的键( ...
- Redis核心原理与实践--散列类型与字典结构实现原理
Redis散列类型可以存储一组无序的键值对,它特别适用于存储一个对象数据. > HSET fruit name apple price 7.6 origin china 3 > HGET ...
- Redis实现之字典
字典 字典,又称为符号表(symbol table).关联数组(associative array)或映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构.在字典中,一个 ...
- 《闲扯Redis十》Redis 跳跃表的结构实现
一.前言 Redis 提供了5种数据类型:String(字符串).Hash(哈希).List(列表).Set(集合).Zset(有序集合),理解每种数据类型的特点对于redis的开发和运维非常重要. ...
- Redis 中的 set 和 sorted set 如何使用,源码实现分析
set 和 sorted set 前言 set 常见命令 set 的使用场景 看下源码实现 insert dict sorted set 常见的命令 使用场景 分析下源码实现 ZADD ZRANGE ...
- 《闲扯Redis七》Redis字典结构的底层实现
一.前言 上节<闲扯Redis六>Redis五种数据类型之Hash型 中说到 Hash(哈希对象)的底层实现有: 1.ziplist 编码的哈希对象使用压缩列表作为底层实现 2.hasht ...
- Redis中5种数据结构的使用场景介绍
转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/108.html?1455861435 一.redis 数据结构使用场景 原 ...
- 深入理解Redis中的主键失效及其实现机制
参考:http://blog.sina.com.cn/s/articlelist_1221155353_0_1.html 作为一种定期清理无效数据的重要机制,主键失效存在于大多数缓存系统中,Reids ...
随机推荐
- python 迭代器和生成器基础知识
1.迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法--字符串.列表.元组.字典.集合都是可迭代的--可以被for循环的都是可迭代的 2. 迭代器有的好处是可以节省内存 3.生 ...
- memcached 如何处理容错的?
不处理! 在 memcached 节点失效的情况下,集群没有必要做任何容错处理.如 果发生了节点失效,应对的措施完全取决于用户.节点失效时,下面列出几种方 案供您选择: 忽略它! 在失效节点被恢复或替 ...
- springboot-mail发邮件,不需要邮件服务器
很简单 步骤走起-> 1.需要一个邮箱账号,我以163邮箱为例,先开启第三方服务后获得密码,后面用来邮箱登录 2.加入mail 依赖 3.properties配置账号和第三方服务密码(不是邮箱密 ...
- 乱序数组中第k大的数(顺序统计量)
该问题是顺序统计量中十分经典的问题. 使用快排中的分区法,将第k大的数排序.若双向扫描分区加上三点中值法或绝对中值法,可以保证在 O(n) 时间里找出第k大的数. 补充:可以直接使用C++STL中的n ...
- 开关电源PCB排版,基本要点分析
1 开关电源PCB排版基本要点 1.1 电容高频滤波特性 图1是电容器基本结构和高频等效模型. 电容的基本公式是 式(1)显示,减小电容器极板之间的距离(d)和增加极板的截面积(A)将增加电容器的电容 ...
- Django ElasticSearch Ionic 打造 GIS 移动应用 —— 架构设计
搜索引擎是个好东西,GIS也是个好东西.当前还有Django和Ionic.最后效果图 构架设计 对我们的需求进行简要的思考后,设计出了下面的一些简单的架构. GIS架构说明 -- 服务端 简单说明: ...
- 破界!Omi生态omi-mp发布,用小程序开发生成Web
omi-mp 是什么 Omi 框架是微信支付线研发部和 AlloyTeam 开源的通用 Web 组件化框架,基于 Web Components,用来开发 PC.手机浏览器或者微信.手Q webview ...
- 利用AudioContext来实现网易云音乐的鲸鱼音效
一直觉得网易云音乐的用户体验是很不错的,很早就注意到了里面的鲸鱼音效,如下图,就是一个环形的跟着音乐节拍跳动的特效. gif动图可能效果不太理想,可以直接在手机上体验 身为前端凭着本能的好奇心和探索心 ...
- java中如何能知道应该捕获什么样的异常?举例
我怎么知道应该捕获什么样的异常? 马克-to-win:如上例1.1:开始没加try时,程序崩溃,系统打印的是如下的错误,Exception in thread "main" jav ...
- java中异常到底有什么用?举例
异常的意义:马克-to-win:通过上面的例子,我们看出通过引入异常这种技术,即使出现不测(用户把0赋给除数),也可以让程序不崩溃,还能继续优雅 的运行.那,这种技术有用,值得学.马克-to-win: ...