【Redis_Day6】Hash类型

【Redis_Day6】Hash类型

  • Hash类型
  • 操作hash的命令
    • hset:设置hash中指定的字段(field)的值(value)
    • hsetnx:想hash中添加字段并设置值
    • hget:获取hash中指定字段的值
    • hexists:判断hash中是否有指定的字段
    • hdel:删除hash中指定的字段
    • hkeys:获取hash中所有的字段
    • hvals:获取hash中所有的值
    • hgetall:获取hash中所有字段和其对应的值
    • hmget:一次获取hash中多个字段的值
    • hlen:获取hash中所有字段的个数
    • hincrby:给hash中指定字段对应的值 加n(n是整数)
    • hincrbyfloat:给hash中指定字段对应的值 加n(n是浮点数)
    • hash相关命令小结
  • hash内部编码
  • hash的应用
    • 作为缓存
      • 三种缓存方式对比
  • 高内聚低耦合

Hash类型

⼏乎所有的主流编程语⾔都提供了哈希(hash)类型,它们的叫法可能是哈希、字典、关联数组、映射。

在Redis中,哈希类型是指值本⾝⼜是⼀个键值对结构,形如key="key",value={{field1,value1},…,{fieldN,valueN}}。field不能重复。
哈希类型中的映射关系通常称为field-value,⽤于区分Redis整体的键值对(key-value),使用过程中要注意value在不同上下⽂的作⽤。

如下是字符串和哈希类型的对比:
在这里插入图片描述
redis自身就是键值对结构。
redis自身的键值对就是通过hash的方式来组织的。
针对一个键值对来说,其中的value同样可以是键值对,该键值对的类型是hash。

操作hash的命令

使用下列h系列命令的前提:key对应的value必须是hash类型。

hset:设置hash中指定的字段(field)的值(value)

总结hset的用法:
hset key field value [field value ...]:设置key的值,值的类型是hash。对一个key,可以一次给它设置多个field-value。返回值是添加的字段field的个数。如果key不存在,先创建key。
field-value中的value要求是字符串类型。
在这里插入图片描述在hset命令中,设置字段field1的值时,如果field1存在,那么本次设置的value会覆盖field1原来的值。

hsetnx:想hash中添加字段并设置值

hsetnx命令也是给字段设置值,但是要求字段不存在才能设置成功,也就是在hash中新建字段field1并给它设置值。

总结hsetnx的用法:
hsetnx key field1 value:key中不存在field1,添加field1并设置它的值为value,并返回1。如果field已存在,则操作无效,返回0。
命令成功前提是key存在且key中不存在field1。

在这里插入图片描述

hget:获取hash中指定字段的值

总结hget的用法:
hget key1 field1:获取key1中field1对应的value。返回值是fileld1对应的值或nil。
在这里插入图片描述

hexists:判断hash中是否有指定的字段

总结hexists的用法:
exists key field:去key对应的hash类型的value中找,有没有字段field。找到了返回1,没找到返回0。
在这里插入图片描述

hdel:删除hash中指定的字段

总结hdel的用法:
hdel key field [field ...]:删除key中指定的field,可以一次性删除多个。返回值是本次操作成功删除的字段个数。
在这里插入图片描述

hdel命令是删除key中指定的字段,通用命令del是删除key。

hkeys:获取hash中所有的字段

总结hkeys的用法:
hkeys key:根据key找到对应的hash,然后遍历该hash,获取hash中的所有字段,返回值是字段列表。
在这里插入图片描述

hvals:获取hash中所有的值

总结hvals的用法:
hvals key:根据key找到对应的hash,然后遍历hash,获取hash中所有的值。返回值是值列表。
在这里插入图片描述
hkeys和hvals命令操作起来都是存在一定风险的。这两个命令的时间复杂度都是O(N),N是哈希的元素个数,如果哈希非常大,执行这两个命令的时间会很长,会导致redis服务器堵塞,无法给其他客户端提供服务。

hgetall:获取hash中所有字段和其对应的值

总结hgetall的用法:
hgetall key:遍历key对应的value中所有的field-value,并输出。
在这里插入图片描述hgetall命令也是危险的操作。
在使⽤hgetall时,如果哈希元素个数⽐较多,会存在阻塞Redis的可能。如果开发⼈员只需要获取部分field,可以使⽤hmget,如果⼀定要获取全部field,可以尝试使⽤hscan命令
hscan是渐进式遍历,即hscan命令遍历hash的时候,每次只遍历hash中的一部分,连续执行多次hscan命令,才能完整整个遍历。hkeys,hvals,hgetall三个命令遍历hash的时候都是一次性遍历完全部hash。

hmget:一次获取hash中多个字段的值

总结hmget的用法:
hmget key field [field ...]: 一次获取hash中多个指定字段的值。返回值是对应字段的值或nil。
在这里插入图片描述

hlen:获取hash中所有字段的个数

总结hlen的用法:
hlen key:获取hash中所有字段的个数,返回值是字段的个数。
在这里插入图片描述执行hlen命令是不用遍历的。

hincrby:给hash中指定字段对应的值 加n(n是整数)

field-value中的value也可以当做数字来处理。

总结hincrby的用法:
hincrby key field incrment:给field对应的value 加上incrment。返回值是field的新值。
在这里插入图片描述

hincrbyfloat:给hash中指定字段对应的值 加n(n是浮点数)

hincrbyfloat是hincrby的浮点数版本。

hash相关命令小结

在这里插入图片描述
在这里插入图片描述
其中,hstrlen key field计算的是field-value中的value的长度。

hash内部编码

hash的内部编码有两种,分别是ziplist(压缩列表)hashtable(哈希表)

压缩的本质是针对数据进行重新编码。
不同的数据,有不同的特点,结合这些特点进行精妙的设计(压缩算法),重新编码之后,就能缩小体积。比如一个字符串abc0000000000000efg,重新编码后字符串变成了abc13[13]efg。

zipllist内部的数据结构也是精心设计的,相比于hashtable存储hash会存在浪费空间的问题,redis内部用ziplist类型存储hash,能节省内存空间。

ziplist的缺点,读写元素时候速度较慢,如果hash中元素个数少,慢的不明显,但是如果元素个数太多,就很慢了。

所以如果哈希中的元素个数比较少,使用ziplist存储,如果hash中元素个数比较多,使用hashtable来存储。
且如果field-value中value都比较短,使用ziplist存储hash,如果某个value的长度过长,也会转换成hashtable存储hash。

通过修改redis配置文件redis.conf中的配置项hash-max-ziplist-entries(默认512个),确定hash中的元素个数超过多少时用hashtable存储hash。
通过修改redis配置文件redis.conf中的配置项hash-max-ziplist-value(默认64字节),确定hash中的元素长度超过多少时用hashtable存储hash。

在这里插入图片描述

hash的应用

作为缓存

redis+mysql组成的缓存存储架构:
在这里插入图片描述
假设把最近使用到的数据作为热点数据存储在redis中,
在上面的场景中:
redis做缓存,应用服务器访问数据,先查询Redis,如果redis上数据存在,就直接从redis上取数据交给应用服务器,不继续访问mysql了。如果redis上数据不存在,再读取mysql,把读到的结果返回给应用服务器的同时,把这个数据也写入到redis中。

用伪代码模拟上述场景,理解hash在其中的应用:
redis用key-value组织用户信息,
key是user:uid,value是hash类型。
在这里插入图片描述
用mysql保存用户信息:
在这里插入图片描述

//当用户发来请求时,假设业务根据用户uid获取用户信息:
//业务层
UserInfo getUserInfo(long uid){//先查询redis,假设用户信息保存在"user:uid"对应的键中。String key = "user:"+uid;//从redis中获取对应的值UserInfoMap value = redis命令:hgetall key;//如果缓存命中(hit)if(value != null){//将映射关系还原成对象形式UserInfo userInfo =  利用映射关系构建对象(UserInfoMap value);return userInfo;}//如果缓存没有命中(miss)//从数据库中,根据uid获取用户信息UserInfo userInfo = mysql执行sql语句:select * from user_info where uid = <uid>;//如果表中没有 uid 对应的⽤⼾信息if(userInfo == null){响应404return null;}//将缓存以哈希类型保存redis执行命令:hmset user:userInfo.uid name userInfo.name age userInfo.age city userInfo.city//写入缓存,为了防止数据腐烂(rot),设置过期时间为1 ⼩时(3600 秒)redis执行命令:expire user:userInfo.uid 3600//返回用户信息return userInfo;
}

比较hash类型和关系型数据库存储用户信息的区别:

  1. 哈希类型是稀疏的,而关系型数据库是完全结构化的。例如哈希类型每个键可以有不同的field,而关系型数据库⼀旦添加新的列,所有行都要为其设置值,即使为null。
    在这里插入图片描述
  2. 关系数据库可以做复杂的关系查询,而Redis去模拟关系型复杂查询,例如联表查询、聚合查询等基本不可能,维护成本高。

三种缓存方式对比

  1. 原生字符串类型⸺⸺使用字符串类型,每个属性⼀个键。
    在这里插入图片描述优点:实现简单,针对个别属性变更也很灵活。
    缺点:占用过多的键,内存占用量较⼤,同时用户信息在Redis中比较分散,缺少内聚性,所以这种方案基本没有实⽤性。

  2. 序列化字符串类型,例如JSON格式
    在这里插入图片描述优点:针对总是以整体作为操作的信息比较合适,编程也简单。同时,如果序列化方案选择合适,内存的使用效率很高。
    缺点:本⾝序列化和反序列需要⼀定开销,同时如果总是操作个别属性则非常不灵活。

  3. hash类型
    在这里插入图片描述优点:简单、直观、灵活。尤其是针对信息的局部变更或者获取操作。
    缺点:需要控制哈希在ziplist和hashtable两种内部编码的转换,可能会造成内存的较大消耗。

比较redis用String类型(Json格式)和用hash类型存储用户信息的区别:

如果用String(JSON)的格式来表示UserInfo,从redis里取出的数据要先通过JSON反序列化转换成对象,从mysql取出的数据要先通过JSON序列化转换成JSON格式。

但是如果用hash类型保存UserInfo,就不用上面两个格外的步骤。且用field表示对象的每个属性,还可以非常方便的修改/获取任何一个属性的值。
当然用hash类型保存UserInfo也有缺点,使用hash类型需要控制hash在ziplist和hashtable两种内部编码的转换,这可能会造成内存的较大消耗。(用空间换时间)

高内聚低耦合

内聚和耦合是衡量代码好坏的标准之一。好的代码应该是高内聚低耦合的。

高内聚:内聚是用来衡量一个模块内部各个元素(如函数、类的方法等)之间的功能相关性和紧密程度的。高内聚意味着模块内的各个部分在功能上高度相关,共同协作完成一个明确的任务。相反,低内聚则表示模块内的各个部分功能不相关或关系松散。
比如上面提到的,把一个用户的所有信息放到hash中一个key中,就是高内聚代码。

低耦合:耦合指的是两个代码/模块之间的关联关系,好的代码追求的是低耦合。模块之间耦合度越高,模块之间越容易相互影响。
比如在分布式系统中引入消息队列就是一个低耦合应用的例子:
假设在一个分布式系统中,A服务器直接调用B服务器,A给B发送请求,B处理请求返回响应给A,此时A和B之间的耦合度是比较大的。
但如果在A和B之间引入消息队列,此时A,消息队列,B三者构成生产者消费者模型,A把请求发给消息队列,B再从消息队列中获取请求,这种情况下如果A出现问题,牵连到B的可能性就很小,A和B之间互连程度降低,即降低了A和B之间的耦合度。


下次见~
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在SQLyog中导入和导出数据库

导入 假如我要导入一个xxx.sql&#xff0c;我就先创建一个叫做xxx的数据库。 然后右键点击导入、执行SQL脚本 选择要导入的数据库文件的位置&#xff0c;点击执行即可 注意&#xff1a; 导入之后记得刷新一下导出 选择你要导出的数据库 右键选择&#xff1a;备份/导出、…

详细教程-Linux上安装单机版的Hadoop

1、上传Hadoop安装包至linux并解压 tar -zxvf hadoop-2.6.0-cdh5.15.2.tar.gz 安装包&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1u59OLTJctKmm9YVWr_F-Cg 提取码&#xff1a;0pfj 2、配置免密码登录 生成秘钥&#xff1a; ssh-keygen -t rsa -P 将秘钥写入认…

实时数据开发 | 怎么通俗理解Flink容错机制,提到的checkpoint、barrier、Savepoint、sink都是什么

今天学Flink的关键技术–容错机制&#xff0c;用一些通俗的比喻来讲这个复杂的过程。参考自《离线和实时大数据开发实战》 需要先回顾昨天发的Flink关键概念 检查点&#xff08;checkpoint&#xff09; Flink容错机制的核心是分布式数据流和状态的快照&#xff0c;从而当分布…

鸿蒙网络编程系列50-仓颉版TCP回声服务器示例

1. TCP服务端简介 TCP服务端是基于TCP协议构建的一种网络服务模式&#xff0c;它为HTTP&#xff08;超文本传输协议&#xff09;、SMTP&#xff08;简单邮件传输协议&#xff09;等高层协议的应用程序提供了可靠的底层支持。在TCP服务端中&#xff0c;服务器启动后会监听一个或…

DataGrip 连接 Redis、TongRDS

连接 Redis 或 TongRDS 有些旧版本 没有 redis 驱动用不了 1&#xff09;选择驱动 2&#xff09;添加连接信息 3&#xff09;测试连接 4&#xff09;保存连接 5&#xff09;使用案例

DevExpress控件 基本使用

DevExpress控件 一、DevExpress简介 1、所有编辑器的公共功能 全部都可以绑定数据&#xff1b; 全部都可以独立使用或用于由 Developer Express 提供的容器控件 (XtraGrid、XtraVerticalGrid、XtraTreeList 和 XtraBars) 内的内置编辑&#xff1b; 全部都使用相同的样式、外…

mybatis学习(一)

声明&#xff1a;该内容来源于动力节点&#xff0c;本人在学习mybatis过程中参考该内容&#xff0c;并自己做了部分笔记&#xff0c;但个人觉得本人做的笔记不如动力节点做的好&#xff0c;故使用动力节点的笔记作为后续mybatis的复习。 一、MyBatis概述 1.1 框架 在文献中看…

C0034.在Ubuntu中安装的Qt路径

Qt安装路径查询 在终端输入qmake -v如上中/usr/lib/x86_64-linux-gnu就是Qt的安装目录&#xff1b;

线程控制方法之wait和sleep的区别

线程控制方法之wait和sleep的区别 wait()和sleep()都是Java线程控制方法&#xff0c;但存在明显区别&#xff1a; 所属与调用&#xff1a;wait()属Object类&#xff0c;需synchronized调用&#xff1b;sleep()属Thread类&#xff0c;可随意调用。锁处理&#xff1a;wait()释放…

Harbor2.11.1生成自签证和配置HTTPS访问

文章目录 HTTPS的工作流程部署Harbor可参考上一篇文章生成自签证书1.修改/etc/hosts文件2.生成证书a.创建存放证书路径b.创建ca.key密钥c.创建ca.crtd.创建给Harbor服务器使用密钥 yunzhidong.harbor.com.keye.创建给Harbor服务器使用证书签名请求文件 yunzhidong.harbor.com.c…

Spring Boot3远程调用工具RestClient

Spring Boot3.2之后web模块提供了一个新的远程调用工具RestClient&#xff0c;它的使用比RestTemplate方便&#xff0c;开箱即用&#xff0c;不需要单独注入到容器之中&#xff0c;友好的rest风格调用。下面简单的介绍一下该工具的使用。 一、写几个rest风格测试接口 RestCont…

svn 崩溃、 cleanup失败 怎么办

在使用svn的过程中&#xff0c;可能出现整个svn崩溃&#xff0c; 例如cleanup 失败的情况&#xff0c;类似于 这时可以下载本贴资源文件并解压。 或者直接访问网站 SQLite Download Page 进行下载 解压后得到 sqlite3.exe 放到发生问题的svn根目录的.svn路径下 右键呼出pow…

[Leetcode小记] 3233. 统计不是特殊数字的数字数量

代码&#xff1a; 方法一&#xff1a;平凡解法(最直观但时间复杂度最高) class Solution {public int nonSpecialCount(int l, int r) {int resr-l1;//初始不是特殊数字的答案为[l,r]范围内数字总数for(int i(int)Math.ceil(Math.sqrt(l));i<(int)Math.floor(Math.sqrt(r))…

jenkins 2.346.1最后一个支持java8的版本搭建

1.jenkins下载 下载地址&#xff1a;Index of /war-stable/2.346.1 2.部署 创建目标文件夹&#xff0c;移动到指定位置 创建一个启动脚本&#xff0c;deploy.sh #!/bin/bash set -eDATE$(date %Y%m%d%H%M) # 基础路径 BASE_PATH/opt/projects/jenkins # 服务名称。同时约定部…

package.json中^1.x.x、~1.x.x、1.x.x有什么区别

目录 包版本号的语义化 包版本号的符号 举例 包版本号的语义化 在开始回答这个问题之前&#xff0c;先简单介绍一下包版本号的语义化。 在npm中&#xff0c;包的版本号通常遵循语义化版本规范&#xff08;Semantic Versioning&#xff09;&#xff0c;即采用 major.minor.p…

el-table :span-method 合并单元格(2.0)

2024.11.23今天我学习了如何使用el-table组件的合并单元格方法&#xff0c;效果如下&#xff1a; 代码如下&#xff1a; <template><div class"container"><el-table :data"table_data" :span-method"object_merge" border>&…

CWT-CNN-SABO-LSSVM | Matlab实现基于CWT-CNN-SABO-LSSVM对滚动轴承的故障诊断

CWT-CNN-SABO-LSSVM | Matlab实现基于CWT-CNN-SABO-LSSVM对滚动轴承的故障诊断 目录 CWT-CNN-SABO-LSSVM | Matlab实现基于CWT-CNN-SABO-LSSVM对滚动轴承的故障诊断分类效果基本描述程序设计参考资料 分类效果 基本描述 基于CWT-CNN-SABO-LSSVM对滚动轴承的故障诊断 matlab代码…

Java基于SSM框架的校园综合服务小程序【附源码、文档】

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

WPF——ICON按钮制作

前言 首先ICON按钮&#xff0c;即带图标按钮&#xff0c;即图标按钮。 图标按钮在开发时&#xff0c;主要是有两种方式来进行。一是在Button的Content内添加Image&#xff0c;然后设置Image的属性Source来实现&#xff0c;这种方式主要是简单易操作&#xff0c;对于初学者来说…

LlamaIndex ollama 搭建本地RAG应用,建立本地知识库

目录 简介安装前的准备下载ollama创建llamaindex conda环境&#xff0c;为后面编码作准备 环境变量迁移ollama到其他盘运行ollama方式一方式二禁止ollama开机自启动运行第一个模型 Chatbox聊天下载Chatbox配置ollama地址和模型验证 建立自身特定知识数据搭配大语言模型创建项目…