前言

  • 相信你一定使用过新华字典吧!小时候不会读的字都是通过字典去查找的。在Redis中也存在相同功能叫做字典又称为符号表!是一种保存键值对的抽象数据结构

  • 本篇仍然定位在【redis前传】系列中,因为本篇仍然是在解析redis数据结构!当你尝试去了解redis时才能明白其中原理!才能明白为什么redis被大家吹捧速度快,而不是被告知redis很快!

应用场景

  • 在Redis中有很多场景都是用了字典作为底层数据结构!我们使用最多的应该是redis的库的设置和五种基本数据类型的Hash结构数据!
  • 在上一篇【redis前传】中我们学习了list数据结构。今天我们继续学习主流数据结构Hash。
  • 在redis内部有字典结构、hash结构但是这里的hash和我们平时熟知的redis基础数据的hash并不是一个意思!我们简单的将字典结构、hash结构理解成redis更加底层的一种抽象结构。平时我们使用的hash基础数据结构理解成hash工具

  • 而今天我们的主角就是五种数据结构的Hash分析。他的底层使用了字典这个结构。字典结构内部使用的是底层的hash结构。有点绕!好好理解你行的

哈希表

  • 上面这张图诠释了作为redis底层结构的Hash。在内部redis称之为dictht 。 后面我们为什么和之前的hash结构冲突我们都已类名为准叫做dictht类。
  • 在hictht类中有四个属性分别是table 、 size 、 sizemask 、 used ; 其中table就是一个数组;数组中元素是另外一个类叫做dictEntry类。
  • dictEntry就是真正存储数据的。内部是key、value存储结构。一个简单的哈希表就如图所示。数据最终会存储在table中的dictEntry对象中。
  • 至于为什么sizemask = size -1 ; 这个是为了在计算hash索引时需要用到的。那为什么不直接使用size-1而是通过一个变量来承接呢?这个吧!!!我也不知道。容我去百度百度。

数组节点

  • 上面的哈希表是不是很熟悉,这不和我们Java中的Map数据结构如出一辙吗。可以说是也可以说不是,两者很相似但也有区别的。
  • 在上面中我们提到数据最终是存储在哈希表里table数组里的元素。该元素叫dictEntry 。 下面我们看看dictEntry结构如何吧!

  • 通过左侧对dictEntry的定义我们可以看出。dictEntry存储的值可以是指针、正数、浮点数各种数据类型!类似于Java中的Object属性。 对于上述的key没有啥真意的就是一个键。
  • 既然是数组那么索引就是固定长度的,那么在有限的长度中肯定会出现经典问题就是【hash冲突】。在Java中我们是通过链表和红黑树来解决冲突的问题!在redis中是通过链表解决的。在dictEntry中通过next指针将冲突元素连接。
  • 这里我们就可以和Java中的Map结构进行理解。他们内部很是相似!!!
  • 这里需要注意下在hash冲突时redis的确是通过链表进行存储的,但是由于哈希表(dictht)中没有记录每个索引未中链表的尾部节点只有头结点信息所以。而且我们也知道链表在查询上效率不佳,所以当发生哈希冲突时redis是将新加入的节点加入在链表的头部!

字典

多态字典

  • 字典是本文开头提出的结构!也是redis中大量使用的一种底层数据结构。在redis中名叫做dict类。

  • 通过图示我们明确的看出内部是包含哈希表的。其实从名字上我们也可以看出哈希表为什么叫dictht 。 笔者这里认为是dicthashcodetable 。 意思就是字典表内部的一个hash相关的数组(仅个人理解)
  • 之前也提到过redis内部很多地方会使用到字典!就好比我们上学是用到【新华字典】、【成语词典】、【歇后语词典】等等。虽然名字叫法不一样但是内部结构都是一部字典供我们快速定位。而redis中dict内部就是通过type字段进行区分每个字典的。而privdata是每部字典需要的特定参数。通过type和privdata就可以轻松实现各种功能不同的字典,他有个专有名词叫多态字典

rehash

  • 除了type 、 privdata以外剩下的就是ht 、 rehashidx了。其中ht是一个长度为2的数组。数组里元素就是我们之前提到了哈希表(dictht) 。 ht为什么长度为2 这就需要我们了解下redis的rehash过程了。而rehashidx就是记录rehash的进度!在没有发生rehash的时候rehashidx=-1;
  • 在实际使用过程中在字典中我们所有的数据都会存储在ht[0]对应的哈希表中。ht[1]永远都是一个空数组。这些都是为什么rehash做准备,在正式开始之前我们先来了解下redis为什么需要rehash这个动作
  • 首先我们在哈希表中是一个定长数组发生冲突时内部是通过链表解决的。理论上一个哈希表可以存储足够的数据,这里的足够就是指空间允许的范围有多少存多少。但是我们知道链表的特点就是新增、删除很快但是查询很慢,尤其是当链表很长的时候就会出现查询效率低下的问题!为了避免链表过长redis就会在一定条件下对哈希表中数组长度的扩展从而解决局部链表过长的问题!
  • 每次数组发生长度变化时,那么之前的hash值就需要重新经历一遍hash然后寻址index的过程。这个过程就叫做rehash

  • 关于rehash和Java中Map的resize是一样的功能!Java中resize是直接new 出一片内存进行复制的而且他是每次进行2倍扩展。而redis的rehash稍微不同基本上我们也可以理解成2倍扩展!关于两块内存复制有点类似于JVM中垃圾回收有点类似。有时间我们可以一起研究下JVM章节。
  • 那么啥时候需要进行rehash呢?这里和Java的负载因子一样;但是除了负载因子这个空间考核以外redis还考虑一个性能的问题。因为在单线程的前提下我们还要考虑客户端使用的感知性!单线程的意思就是执行命令是顺序执行的。总不能在我们rehash的过程中全部阻塞客户端的使用这对于操作体验上稳定性来说是不友好的。

  • 涉及到上述两个命令的我们称之为后台命令结合负载因子产生如下条件

渐进式rehash

  • 一直强调redis是单线程。那么什么叫单线程模型?就是对于redis服务来说执行命令是线性操作!但是每个客户端的命令是无序的,先到的就先进入队列redis服务从队列一次取出命令进行执行。除了客户端的命令还有一些系统生成的命令比如说我们上面提到的rehash操作!

  • ①、首先为了避免阻塞客户端或者说尽量控制阻塞的时间在客户端感知范围内,redis内部的rehash并不是一次性操作而是一个循序渐进的过程。一次仅复制一部分

  • ②、还记得之前我们提到dict中rehashidx这个属性吗,他是记录rehash的进度。因为哈希表内部是一个数组而rehashidx就是记录这个数组的索引。从而我们也可以知道每次rehash复制的时候是已一个索引完整链表为单元进行复制的。

  • ③、除了新增以外的其他操作都会同时影响到ht[0]、ht[1] 因为在rehash过程中两个数组都是在使用状态的

  • ④、新增值的时候就只需要新增到ht[1]中。因为最终的目的就是将所有值同步到ht[1]中。而ht[0]的值会慢慢的变少;没必要新增到ht[0]

  • ⑤、在rehash过程中查找元素时会查找两个数组中的并集元素。这也就也是了为什么再rehash过程新增元素只需要新增到ht[1]的原因

总结

①、字典表在redis被广泛使用,基于字典表优秀的设计解决redis单线程问题

②、字典里包含哈希表,哈希表内部使用节点负责存储key、value

③、字典type实现多态字典用于多场景!

④、渐进式rehash解决服务卡顿问题

redis字典快速映射+hash釜底抽薪+渐进式rehash | redis为什么那么快的更多相关文章

  1. 《闲扯Redis八》Redis字典的哈希表执行Rehash过程分析

    一.前言 随着操作的不断执行, 哈希表保存的键值对会逐渐地增多或者减少, 为了让哈希表的负载因子(load factor)维持在一个合理的范围之内, 当哈希表保存的键值对数量太多或者太少时, 程序需要 ...

  2. redis 字典

    redis 字典 前言 借鉴了 黄健宏 的 <<Redis 设计与实现>> 一书, 对 redis 源码进行学习 欢迎大家给予意见, 互相沟通学习 概述 字典是一种用于存储键值 ...

  3. redis字典

    字典作为一种保存键值对的数据结构,在redis中使用十分广泛,redis作为数据库本身底层就是通过字典实现的,对redis的增删改查实际上也是构建在字典之上. 一.字典的结构

  4. docker 一小时快速入门之利用docker安装Redis

    利用docker方式快捷安装redis 该方式默认下载的最新版本镜像,如需要下载指定版本在redis后面跟:版本号 docker pull redis 查看当前下载redis的镜像 docker im ...

  5. redis渐进式rehash机制

    在Redis中,键值对(Key-Value Pair)存储方式是由字典(Dict)保存的,而字典底层是通过哈希表来实现的.通过哈希表中的节点保存字典中的键值对.我们知道当HashMap中由于Hash冲 ...

  6. Redis学习系列四Hash(字典)

    一.简介 Redis中的Hash字典相当于C#中的Hashtable,是一种无序字典,内存存储了很对的键值对,实现上和Hashtable一样,都是"数组+链表"二维结构,都是对关键 ...

  7. redis渐进式 rehash

    转载(http://redisbook.com/preview/dict/incremental_rehashing.html) 上一节说过, 扩展或收缩哈希表需要将 ht[0] 里面的所有键值对 r ...

  8. redis字典的底层实现hashTable

    Redis的字典使用哈希表作为底层实现.一个哈希表里面可以有多个哈希表节点,而每个哈希表节点就保存了字典中的一个键值对 哈希表的数据结构为 table属性是一个数组,数组中的每个元素都是指向dictE ...

  9. 阿里面试官:HashMap 熟悉吧?好的,那就来聊聊 Redis 字典吧!

    最近,小黑哥的一个朋友出去面试,回来跟小黑哥抱怨,面试官不按套路出牌,直接打乱了他的节奏. 事情是这样的,前面面试问了几个 Java 的相关问题,我朋友回答还不错,接下来面试官就问了一句:看来 Jav ...

随机推荐

  1. [bug] maven“1.5不支持diamond运算符,请使用source 7或更高版本以启用diamond运算符”

    原因 maven打包默认采用jdk 1.5,无法识别<> 解决 在pom.xml中加入: <properties> <maven.compiler.source>1 ...

  2. Ubuntu 15.04下安装Docker

    最近听说Docker很火,不知道什么东西,只知道是一个容器,可以跨平台.闲来无事,我也来倒弄倒弄.本文主要介绍:Ubuntu下的安装,以及基本的入门命令介绍:我的机器是Ubuntu 15.04 64位 ...

  3. Play-book格式写法

    Play-Book playbook的组成 play 角色(主机或者主机组) task 任务,演戏的动作 总结:playbook是有多个play组成,一个play有多个task:剧本由一个或者多个演员 ...

  4. win10家庭版升级 到win10企业版

    成功升级3小时  20200124 拿到电脑 win10家庭版 不会用 找admin都找不到只能用企业版 升级win10家庭版 到win10企业版 在msdn下载win10企业版iso iso 文件管 ...

  5. python基础之面向对象(三))(实战:烤地瓜(SweetPotato))

    一.分析"烤地瓜"的属性和方法 示例属性如下: cookedLevel : 这是数字:0~3表示还是生的,超过3表示半生不熟,超过5表示已经烤好了,超过8表示已经烤成木炭了!我们的 ...

  6. MyBatis 高级查询环境准备(八)

    MyBatis 高级查询 之前在学习 Mapper XML 映射文件时,说到 resultMap 标记是 MyBatis 中最重要最强大也是最复杂的标记,而且还提到后面会详细介绍它的高级用法. 听到高 ...

  7. 1.4linux文件和目录常用命令

    文件和目录常用命令 目标 查看目录内容 ls 切换目录 cd 创建和删除操作 touch rm mkdir 拷贝和移动文件 cp mv 查看文件内容 cat more grep 其他 echo 重定向 ...

  8. MyBatisPlus详细总结记录

    本文由 简悦 SimpRead 转码, 原文地址 mp.weixin.qq.com 小 Hub 领读: 一篇写得非常详细的文章,增删改查,各种插件,让你测底熟悉 mybatis plus. 作者:yo ...

  9. 二:使用VS2019 + .net 6创建 webapi 项目

    0.创建一个.net 6项目.由于目前.net 6还是预览版,所以需要添加预览版SDK功能.工具 -> 选项 -> 环境 -> 预览功能 ,勾选使用.net sdk预览版. 1.新建 ...

  10. TVMNN编译Compiler栈

    TVMNN编译Compiler栈 内容纲要 前言 调研目标 TVM介绍 TVM源码架构 FrontEnd Relay BackEnd VTA实现原理及设计思想提炼 整体结构 VTA Hardware ...