点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人。

文章不定期同步公众号,还有各种一线大厂面试原题、我的学习系列笔记。

基础概念

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中的字典结构是怎样的?的更多相关文章

  1. 快速整明白Redis中的字典到底是个啥

    字典简介 字典是一种用于保存键值对的数据结构,可以通过键值对中的键快速地查找到对应的值.在Redis所使用的C语言中,并没有内置字典,所以Redis自己实现了字典. 整个Redis数据库的所有的键和值 ...

  2. Redis数据结构详解(2)-redis中的字典dict

    前提知识 字典,又被称为符号表(symbol table)或映射(map),其实简单地可以理解为键值对key-value. 比如Java的常见集合类HashMap,就是用来存储键值对的. 字典中的键( ...

  3. Redis核心原理与实践--散列类型与字典结构实现原理

    Redis散列类型可以存储一组无序的键值对,它特别适用于存储一个对象数据. > HSET fruit name apple price 7.6 origin china 3 > HGET ...

  4. Redis实现之字典

    字典 字典,又称为符号表(symbol table).关联数组(associative array)或映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构.在字典中,一个 ...

  5. 《闲扯Redis十》Redis 跳跃表的结构实现

    一.前言 Redis 提供了5种数据类型:String(字符串).Hash(哈希).List(列表).Set(集合).Zset(有序集合),理解每种数据类型的特点对于redis的开发和运维非常重要. ...

  6. Redis 中的 set 和 sorted set 如何使用,源码实现分析

    set 和 sorted set 前言 set 常见命令 set 的使用场景 看下源码实现 insert dict sorted set 常见的命令 使用场景 分析下源码实现 ZADD ZRANGE ...

  7. 《闲扯Redis七》Redis字典结构的底层实现

    一.前言 上节<闲扯Redis六>Redis五种数据类型之Hash型 中说到 Hash(哈希对象)的底层实现有: 1.ziplist 编码的哈希对象使用压缩列表作为底层实现 2.hasht ...

  8. Redis中5种数据结构的使用场景介绍

    转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/108.html?1455861435 一.redis 数据结构使用场景 原 ...

  9. 深入理解Redis中的主键失效及其实现机制

    参考:http://blog.sina.com.cn/s/articlelist_1221155353_0_1.html 作为一种定期清理无效数据的重要机制,主键失效存在于大多数缓存系统中,Reids ...

随机推荐

  1. P2P图书馆实践:让知识更好的传播

    人才是每个公司最重要的资产,而人的成长自然就成了最重要的事.苏轼曾经说过:"腹有诗书气自华,代码万行零缺陷",阅读对人成长的影响是巨大的.相信不同的团队都有着自己打造学习氛围.技术 ...

  2. x64 番外篇——知识铺垫

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  3. 怎么得到InnoDB主键索引B+树的高度?

    上面我们通过推断得出B+树的高度通常是1-3,下面我们从另外一个侧面证明这个结论.在InnoDB的表空间文件中,约定page number为3的代表主键索引的根页,而在根页偏移量为64的地方存放了该B ...

  4. 怎样查看一个 linux 命令的概要与用法?假设你在/bin 目录中偶然看到一个你从没见过的的命令,怎样才能知道它的作用和用法呢?

    使用命令 whatis 可以先出显示出这个命令的用法简要,比如,你可以使用 whatiszcat 去查看'zcat'的介绍以及使用简要. [root@localhost ~]# whatis zcat ...

  5. 五、关于mycat踩过的坑

    1.ER分表的从表无法批量插入,例如:insert into tab_a(c1,c2) values(v1,v2),(v11,v21)或者使用jdbctemplate进行batchUpdate操作会报 ...

  6. 全页缓存FPC?

    除基本的会话 token 之外,Redis 还提供很简便的 FPC 平台.回到一致性问题, 即使重启了 Redis 实例,因为有磁盘的持久化,用户也不会看到页面加载速度的 下降,这是一个极大改进,类似 ...

  7. 学习zabbix(八)

    一,Zabbix架构 zabbix 是一个基于 WEB 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案.zabbix 能监视各种网络参数,保证服务器系统的安全运营:并提供灵活的通知机制 ...

  8. springboot-mail发邮件,不需要邮件服务器

    很简单 步骤走起-> 1.需要一个邮箱账号,我以163邮箱为例,先开启第三方服务后获得密码,后面用来邮箱登录 2.加入mail 依赖 3.properties配置账号和第三方服务密码(不是邮箱密 ...

  9. Unity用Input.touches实现手机端多点触控

    多点触控的方法,两边的触控互不干扰: 主要采用Input.touches的相关属性进行操作: 而采用IPointerDrag接口会造成两个drag的相互干扰: 代码如下: using System.C ...

  10. ubantu系统之安装notepadqq

    Ubuntu下的安装方法:     sudo add-apt-repository ppa:notepadqq-team/notepadqq     sudo apt-get update     s ...