三、Redis五种常用数据结构-Hash

Hash是redis中常用的一种无序数据结构。结构类似HashMap。
具体结构如下:key field value

1、优缺点

1.1、优点

  • 同类数据归类整合储存,方便数据管理。
  • 相比于string操作消耗内存和CPU更小。
  • 分字段存储,节省网络流量。

1.2、缺点

  • 过期时间无法设置在field上,只能设置在key上
  • redis集群下不适合大规模使用

2、Hash底层结构

2.1、ziplist-压缩列表

2.1.1、使用条件
  • 哈希对象存储的键值对个数小于512个
  • 哈希对象存储的键值对的键和值的字符串长度小于64字节
2.1.2、数据结构

见list

2.1.3、ziplist的优点
  • 为什么不直接使用hashtable?

相比于hashtable,ziplist结构少了指针,减少了内存的使用。在redis中内存是非常珍贵的。

  • 为什么不使用linkedlist?

ziplist存储时内存地址分配是连续,查询更快。

2.2、dict-字典

字典是在hash存储的数据不满足ziplist中的两个任意一个条件时,使用的数据结构。
由于dict是一种常用的数据结构,但是c语言并不具备此种数据结构,因此redis开发人员自己设计和开发了redisdict结构。详细结构如下:

typedf struct dict{
    dictType *type;//类型特定函数,包括一些自定义函数,这些函数使得key和value能够存储
    void *private;//私有数据
    dictht ht[2];//两张hash表 
    int rehashidx;//rehash索引,字典没有进行rehash时,此值为-1
    unsigned long iterators; //正在迭代的迭代器数量
}dict;
  • typeprivate这两个属性是为了实现字典多态而设置额,当字典中存放着不同类型的值,对应的复制、比较函数也是不一样,这两个字段组合起来可以实现多态的方法调用。
  • ht[2],两个hash
  • rehashidx,辅助变量,用于记录rehash过程的进度,以及是否正在进行rehash等信息。当此值为-1时表示该dict没有进行rehash操作。
  • iterators,记录此时dict有几个迭代器正在进行遍历过程。
2.2.1、dictht-哈希表

dict结构上可以看出,dict实际上就是对dictht的操作,dictht的具体结构如下:

typedf struct dictht{
    dictEntry **table;//存储数据的数组 二维
    unsigned long size;//数组的大小
    unsigned long sizemask;//哈希表的大小的掩码,用于计算索引值,总是等于//size-1
    unsigned long used; 哈希表中中元素个数
}dictht;
  • table是一个dictEntry类型的数组,用户真正存储数据。
  • size表示**table这个数组的大小。
  • sizemask用于计算索引的位置,总是等于size-1
  • used表示**table数组中已有的节点个数。
2.2.2、dictEntry

上面分析dictht实际存储数据的是dictEntry数组,其结构定义如下:

typedf struct dictEntry{
    void *key;//键
    union{
        void val;
        unit64_t u64;
        int64_t s64;
        double d;
    }v;//值
    struct dictEntry *next;//指向下一个节点的指针
}dictEntry;

整个dict字典的结构示意图如下:
image.png

2.2.3、扩容与缩容

当哈希表的数量主键增大时,此时添加数据,产生hash冲突的概率主键增大,且dict也是采用拉链法解决hash冲突的,因此随着hash冲突的增加,链表的长度也在逐渐增大。这时查询的速度会随着链表的长度主键变慢。相反,当元素主键减少时,元素占用dict的空间逐渐减少,处于对内存的极致利用,此时就需要进行缩容操作。
dict的扩容和缩容操作有一点和Java中的HashMap结构类似,都有负载因子。负载因子一般用于表示集合当前被数据填充的程度。在Redis的字典dict中,负载因子=哈希表已存节点数量/哈希表长度,即:

load factor=ht[0].used/ht[0].size

Redis中,关于扩容和缩容有三条规则:

  • 没有执行BGSAVEBGREWRITEAOF指令的情况下,哈希表的负载因子大于等于1时进行扩容。
  • 正在执行BGSAVEBGREWRITEAOF指令的情况下,哈希表的负载因子大于等于5时进行扩容。
  • 负载因子小于0.1时,Redis自动对哈希表进行缩容操作。

Redis扩容和缩容的数量规则:

  • 扩容后:扩容后的dictEntry数组数量为第一个大于等于ht[0].used*22^n;
  • 缩容后:缩容后的dictEntry数组数量为第一个大于等于ht[0].used2^n;
2.2.4、rehash

Redis的扩容或者缩容,与Java中的HashMap类似都有rehash过程。Java中的HashMaprehash过程如下:

  1. 新建一个哈希表,一次性将当前的数据全部rehash,然后复制到新的哈希表上。
  2. 舍弃掉原来的哈希表,而持有新的hash表。这个过程是一个时间复杂度为O(n)的操作。

对于单线程的Redis而言很难承受这么高的时间复杂度的操作。因此Redisrehash操作相比较于HashMap有所不同。Redis采用渐进式rehash的方式。其过程如下:

  1. 假设当前数据在ht[0]上,那么首先会为ht[1]分配到足够的空间。如果是扩容ht[1]就按照扩容规则进行设置。如果是缩容ht[1]就按照缩容规则设置。
  2. dict结构中有个rehashidx字段,用来记录rehash的位置。**rehash=0,**表示rehash开始。
  3. rehash进行期间,每次对字典进行添加、删除、查找、更新操作时,除了执行指定的操作外,还会顺带将ht[0]哈希表上的数据rehashht[1]上,每rehash一个ht[0]上的数据到ht[1]rehashidx都会加1。每次顺带的rehash操作只会搬移少量的数据(100个元素)。
  4. 随着字典操作的不断进行,在某个时刻,ht[0]上的所有数据全部被rehashht[1]上,这时rehashidx的值为-1,表示rehash的操作已完成。

以上就是Redis中的dict渐进式rehash过程,但是这个过程存在两个问题:

  1. 在第三步说了,每次在对字典执行增删改查时才会触发rehash过程。万一某一时间段,一直都没有请求怎么办?

A:Redis中有一个定时器,会定时去判断rehash是否完成,如果没有完成,则继续进行rehash操作。

  1. rehash过程中维护两个hash表,是如何对外提供服务的?

A:对于添加操作,会将数据直接添加到ht[1]上,这样就会保证ht[0]上的数据只会减少不会增加。而对于**删除、更改、查询操作。**会直接在ht[0]上进行操作,尤其这三个操作都会涉及到查询,当在**ht[0]**上查询不到时,会接着去**ht[1]**上查找,如果在找不到,则表示此key不存在。

2.2.5、渐进式rehash的优缺点
  • 优点:采用分而治之的思想,将rehash操作分散到每一个对该哈希表的操作上以及定时函数上,避免了集中式的rehash带来的性能压力。
  • 缺点:在rehash期间内,需要保存两个hash表,对内存的占用稍大,而且如果在redis服务器内存满了的时候,突然进行**rehash**操作,会造成大量key被抛弃
2.2.6、思考题

为什么扩容时需要考虑BGSAVE的影响,而缩容时不需要?

  • BGSAVE时,dict进行扩容,则此时就需要为ht[1]分配内存,若是ht[1]的数据量很大时,就会占用更多的系统内存,造成内存页过多分离,所以为了避免系统耗费更多的开销去回收内存,此时最好不要进行扩容。
  • 缩容时,结合缩容的条件,此时负载因子<0.1,说明此时的dict中的数据很少,就算为ht[1]分配内存,也消耗不了多少资源。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/604814.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java数组的使用

前言 这里我使用的是IDEA编译器进行演示 数组的创建与初始化 创建格式&#xff1a; T[] 数组名 new T[N] T表示数组存放的数据类型&#xff0c;N表示数组的大小。 T[] 表示数组的类型。 这里要注意和C语言不同的是C语言使用类似int arr[10]这样的结构进行创建数组&#xff0c…

24V转3.8V用什么芯片方案-AH8310

在将24V降压至3.8V的电源转换中&#xff0c;AH8310是一个理想的选择。这款芯片是一款降压转换器&#xff0c;输入电压范围为4.5V至36V&#xff0c;输出电压可调&#xff0c;峰值电流可达1.5A。AH8310采用SOT23-6封装&#xff0c;内置MOS&#xff0c;适用于各种应用场合&#xf…

modprobe: can‘t open ‘modules.dep‘: No such file or directory

使用modprobe会提示modprobe: cant open modules.dep: No such file or directory 直接输入depmod即可。 如果depmod没有效果&#xff0c;则需要重新配置编译你的根文件。 在busybox配置界面进入linux Module Utilities, 上下键选择depmod&#xff0c;并按 y 选中&#xff0c…

【vue+vue-treeselect】根据指定字段,如isLeaf(是否末级节点),设置只允许末级节点可以选

1、当项目有特殊要求&#xff0c;必须根据某个字段的值去判断&#xff0c;是否节点可以选&#xff0c;即使已经是末级节点了&#xff0c;还是需要根据字段判断是否禁用 &#xff08;1&#xff09; :flat"true"一定要设置 (2)获取数据源的时候&#xff0c;设置下禁用…

leetcode91.解码方法(动态规划)

问题描述&#xff1a; 一条包含字母 A-Z 的消息通过以下映射进行了 编码 &#xff1a; A -> "1" B -> "2" ... Z -> "26" 要 解码 已编码的消息&#xff0c;所有数字必须基于上述映射的方法&#xff0c;反向映射回字母&#xff08;可…

NineData亮相2024中国移动算力网络大会

4月28日至29日&#xff0c;2024中国移动算力网络大会在苏州召开。大会以“算力网络点亮AI新时代”为主题&#xff0c;全面展示了中国移动最新算力网络成果与能力。江苏省委常委、苏州市委书记刘小涛&#xff0c;副省长赵岩出席开幕式并致辞。内蒙古自治区副主席白清元出席。中国…

【JAVA语言-第20话】多线程详细解析(二)——线程安全,非线程安全的集合转换成线程安全

目录 线程安全 1.1 概述 1.2 案例分析 1.3 解决线程安全 1.3.1 synchronized关键字 1.3.1.1 同步代码块 1.3.1.2 同步方法 1.3.2 使用Lock锁 1.3.2.1 概述 代码示例 1.4 线程安全的类 1.4.1 非线程安全集合转换成线程安全集合 线程安全 1.1 概述 指如果有多…

JavaEE企业级开发中常用的JDK7和JDK8的时间类

JDK7时间类 全世界的时间有一个统一的计算标准 在同一条经线上的时间是一样的 格林威治时间 简称GMT 计算核心 地球自转一天是24小时 太阳直射正好是12小时 但是误差太大 现在用原子钟来代替 用铯原子震动的频率来计算时间&#xff0c;作为世界的标准时间UTC 中国标准时间…

Dockerfile实践java项目

目的&#xff1a;用java项目测试dockerfil部署&#xff08;前提是安装好了docker&#xff09; 部署准备文件如下 1. java项目 java项目demo地址 https://gitee.com/xiaoqu_12/dockerfileDemo.git 或者百度网盘直接下载打包好的jar包 链接&#xff1a;https://pan.baidu.com/s/…

Ansible---inventory 主机清单

一、inventory 主机清单 1.1、inventory介绍 hosts配置文件位置&#xff1a;/etc/ansible/hosts Inventory支持对主机进行分组&#xff0c;每个组内可以定义多个主机&#xff0c;每个主机都可以定义在任何一个或多个主机组内。 1.2、inventory中的变量 Inventory变量名含义…

数值计算方法——大题题型总结

目录 一、绝对误差限、相对误差限 1.1 例题 1.2 解题套路 1.3 题解 二、敛散性、收敛速度 2.1 例题 2.2 解题套路 2.3 题解 三、牛顿迭代法 3.1 例题 3.2 解题套路 3.3 题解 四、割线法 4.1 例题 4.2 解题套路 ​4.3 题解 五、列主元素消去法 5.1 例题 5.…

新版Idea配置仓库教程

这里模拟的是自己搭建的本地仓库环境&#xff0c;基于虚拟机搭建利用gogs创建的仓库 1、Git环境 你需要准备好git和仓库可以使用github 、gitee等 1.1 拉取代码 本项目使用 Git 进行版本控制&#xff0c;在 gogs 上创建一个个人使用的 git 仓库&#xff1a; http://192.168.…

【Linux】项目自动化构建工具make/makefile的简单使用

使用步骤 1) 编写 创建 makefile 文件 vim makefile用 vim 打开名为 makefile 的文件,存在该文件则打开编辑,不存在则创建并打开.在 makefile 文件中编写需要编译的文件 test:test.cppg -o test test.cpp第一行: 冒号左侧为编译后的可执行文件名,可以随便取. 冒号右侧为依赖…

vue2项目升级到vue3经历分享4

后端重构&#xff0c;如果接口做好抽象封装&#xff0c;只需要考虑jar之间的兼容性问题&#xff0c;jdk版本不变&#xff0c;基本不用做太大的调整&#xff0c;但是前端就不一样&#xff0c;除了vue框架本身&#xff0c;css的调整&#xff0c;改起来更是让人头疼。前面写了vue2…

Linux与windows网络管理

文章目录 一、TCP/IP1.1、TCP/IP概念TCP/IP是什么TCP/IP的作用TCP/IP的特点TCP/IP的工作原理 1.2、TCP/IP网络发展史1.3、OSI网络模型1.4、TCP/IP网络模型1.5、linux中配置网络网络配置文件位置DNS配置文件主机名配置文件常用网络查看命令 1.6、windows中配置网络CMD中网络常用…

C++进阶之路:深入理解编程范式,从面向过程到面向对象(类与对象_上篇)

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

Mysql 基础 - 常见 子句

算数运算符 > < > < !/<> 逻辑运算符 3i in is null is not null 2l limit like 2o or 、order by 1a and ib between and 1n not and、or 、not、 in、 orderby、 limit、 like、 between...and、 is null 、is not null

我独自升级崛起怎么下载 游戏下载教程分享

《我独自升级&#xff1a;崛起》这款游戏核心聚焦于激烈的战斗与角色的持续成长。新加入的玩家首要任务是熟悉基础攻击模式&#xff0c;随后深入探索技能组合策略与连贯招式的艺术&#xff0c;同时掌握防守与躲避技巧&#xff0c;这些都是战斗中不可或缺的关键。随着战斗的持续…

那个在买珠宝的年轻人

金价搭上过山车&#xff0c;今年以来价格一路飙涨。 珍珠身价同步飙升&#xff0c;晋级珠宝圈“新宠”。 文玩圈“减龄”&#xff0c;盘珠串不再只是“老头乐”。 月薪3000的年轻人&#xff0c;悄悄实现“宝石”自由。 黄金珠宝走俏&#xff0c;这届年轻人到底有着怎样的珠宝…

Baidu Comate智能编码助手 -----AI编程帮你解放双手

目录 Baidu Comate是什么&#xff1f; Baidu Comate如何安装&#xff1f; 在VSCode上安装Baidu Comate插件 Baidu Comate如何使用&#xff0c;有哪些功能&#xff1f; 1.代码解释 2.代码注释 使用感受 如何体验 Baidu Comate是什么&#xff1f; Baidu Comate智能编码助手…
最新文章