当前位置: 首页 > news >正文

redis的数据类型:Hash

文章目录

    • Hash类型介绍
      • 我要存一个uid为1的用户对象,姓名为James,年龄是28,如果我想用Redis中的string类型来存,应该怎么存?如果我想用Redis中的Hash类型来存,又应该怎么存?
      • 哈希类型中的映射关系通常称为field-value,为什么这里不用key-value?
      • 请问一个key如果对应的value类型是hash<field,value>,那么key field value三者之间的对应关系是怎样的?是一对一对一吗?
    • Hash类型的命令
      • HSET
      • HGET
        • 如何获得一个Hash表中field1对应的value?
        • 如果hget key field指令中,key或者field不存在,结果会怎样?
      • hexists
      • Hdel
        • 如何删除hashtable中的一个<field,value>键值对?
        • 如果我语句中指定的key和field中有无效的参数,返回的结果是什么?
        • 如何删除一个hashtable?
      • HKEYS
      • hvals
      • hgetall
      • HMGET
      • HSCAN
      • Hlen
      • hincrby / hincrbyfloat
      • HSTRLEN
    • Hash类型的内部编码
      • 对压缩的理解
      • 为什么要用ziplist存储hashtable?
      • hash类型的value什么时候用ziplist存?什么时候用hashtable存?
    • Hash类型的使用场景
    • Hash类型与关系型数据库的区别是什么?

Hash类型介绍

我要存一个uid为1的用户对象,姓名为James,年龄是28,如果我想用Redis中的string类型来存,应该怎么存?如果我想用Redis中的Hash类型来存,又应该怎么存?

在这里插入图片描述

哈希类型中的映射关系通常称为field-value,为什么这里不用key-value?

主要是用于区分Redis整体的键值对(key-value),注意这里的value是指field对应的的值,不是键(key)对应的的值,请注意value在不同上下文的作用。

请问一个key如果对应的value类型是hash<field,value>,那么key field value三者之间的对应关系是怎样的?是一对一对一吗?

一个Key可以对应多个field,一个field对应一个value,key field value三者对应关系是1:n:n

Hash类型的命令

HSET

如何向一个hashtable中插入一个键值对?
使用HSET key field1 value1 field2 value2 ...
注意上面指令中,Hash键值对中的value类型就只能是string了
HSET指令的返回值为设置成功的键值对个数
HSETNX key field value 同样也可以设置键值对,但是有一定的条件,即NX:只有当key对应的Hashtable中field不存在时,才能创建成功

HGET

如何获得一个Hash表中field1对应的value?

使用指令hget key field1
结果返回指定key 和field对应的value

如果hget key field指令中,key或者field不存在,结果会怎样?

结果返回nil

hexists

如何判断指定key和field是否存在?(如何判断这个key+field的组合有没有一个value与之对应?)
通过执行指令hexists key field
结果返回值:一般查找成功返回1,不成功返回0

Hdel

如何删除hashtable中的一个<field,value>键值对?

hdel key field1 filed2...
key指定要删除的是哪一个hashtable中的键值对
field指定的是hashtable中某个具体的键值对

Hdel指令执行完毕之后,结果返回成功删除的键值对个数

如果我语句中指定的key和field中有无效的参数,返回的结果是什么?

返回0,表示成功删除的键值对个数是0

如何删除一个hashtable?

直接del key
其中key就是这个hashtable对应的key

HKEYS

如何查看hashtable中有哪些field?
HKEYS key(其中key就是这个hashtable对应的key)
这个操作,先根据key找到对应的hash,时间复杂度为O(1)
然后再遍历hash,打印出hash中的每个field,时间复杂度是O(N)

谈到O(N) ,这个N的含义还挺复杂的

  • 有的时候,N表示redis中所有key的个数
  • 有的时候,N表示redis指令中key的个数,比如HSET key field1 value1 field2 value2中,key的个数就是两个
  • 有的时候,N表示当前key对应的value里面的元素个数.(比如value的类型是hash,N就表示hash中的键值对个数)

注意!! 这个操作也是存在一定的风险的!! 类似于之前介绍过的 keys *(keys *是要打印出redis中的所有key),而HKEYS key是要打印出key对应hashtable中的所有field,如果 这个hash 中存在大量的 field,那也是很要命的,单线程Redis就被阻塞了

hvals

如何查看一个hashtable中的所有value?
hvals key(其中key就是这个hashtable对应的key)
和 hkeys 相对,hvals key能够获取到 hash 中的所有 value
H 系列的命令 必须要保证 key 对应的 value 得是哈希类型的!!
这个操作的时间复杂度,也是O(N),N是哈希的元素个数.如果哈希非常大,这个操作就可能导致redis服务器被阻塞住.

hgetall

如何查看hashtable中的所有键值对?
hgetall key
其中key就是这个hashtable对应的key
上述这样的操作,耗时还是比较大的。多数情况下,我们不需要查询所有的field,可能只想查其中的几个field,这时候我们就用HMGET

HMGET

如何查看hashtable中的多个键值对?
hmget key field1 field2 field3 ...
HMGET 类似于之前的MGET,可以一次查询多个field,HGET一次只能查一个field
有没有hmset,一次设置多个field和value呢??
有, 但是,并不需要使用. 因为hset已经支持一次设置多个field和value了.

HSCAN

上述hkeys, hvals, hgetall都是一条命令能完成所有的遍历操作,存在一定风险:如果hash的元素个数太多,执行的耗时会比较长,从而阻塞redis.
hscan遍历redis的hash. 但是它属于 “渐进式遍历”
=> 即敲一次命令,遍历一小部分. 再敲一次,再遍历一小部分. …… 连续执行多次,就可以完成整个的遍历过程了.
=> 时间就是可控的 化整为零
这也可以联想到ConcurrentHashMap:线程安全的哈希表,这个哈希表在扩容的时候,也是按照化整为零的方式进行的!!

C++ 的标准库,横向和其他编程语言比,就是个 弟中弟 ,C++ 标准库的设计精妙,实现也是很优雅的,但是从功能角度,C++ 标准库给咱们提供的功能太少了~~C++ 提供的各种容器,都是线程不安全的.Java 标准库则直接提供了一些线程安全的集合类(Java 中也有 “容器” 这样的术语,指的是别的了)

Hlen

如何查看一个hashtable中有多少个键值对?
HLEN key
在这里插入图片描述

O(1)获取hash的元素个数,不需要遍历~~

hincrby / hincrbyfloat

请问如何对Hashtable中的value进行加减操作?
hincrby / hincrbyfloat 指令
hincrby 就可以加减整数.使用格式为hincrby key field + num,其中key field用来指定一个hashtable中的value,num表示加数,NUM为负表示减数
hincrbyfloat 就可以加减小数.使用格式为hincrbyfloat key field + float,其中key field用来指定一个hashtable中的value,float表示要加的浮点数,为负表示要减的浮点数

HSTRLEN

如何计算哈希表中指定字段(field)对应的 value 的字符串长度?
HSTRLEN key field

Hash类型的内部编码

Hash类型的内部编码主要就是hashtableziplist两种,我们前面说过,hashtable的实现方式就和我们数据结构中理解的哈希表实现并无太大差别,主要就是基于数组和映射规则实现的,重点在于ziplist,为什么要引入ziplist?这种存储方式有什么好处呢?

对压缩的理解

一些具体的压缩算法:rar, zip, gzip, 7z……
举个例子:
在这里插入图片描述

压缩的本质,是针对数据进行重新编码。不同的数据,有不同的特点. 结合这些特点,进行精妙的设计,重新编码之后,就能够缩小体积。比如上图中,00...0在经过编码之后,就可以用0[100]来代替

为什么要用ziplist存储hashtable?

hash 首先是一个数组~~数组上有些位置有元素,有些没有元素,对于那些没有存放元素的映射位置,这些空间就被浪费了。所以我们引入了ziplist作为Hash类型的一种内部编码方式,这样就可以减少空间的浪费。
但凡事都是有代价的,ziplist 付出的代价就是ziplist进行读写的速度比较慢,并不能达到理想的O(1),如果元素个数少,慢的并不明显。如果元素个数太多了,慢就会雪上加霜.

hash类型的value什么时候用ziplist存?什么时候用hashtable存?

哈希中的元素个数比较少,使用 ziplist 表示。元素个数比较多,使用 hashtable 来表示。这个元素多少的界限值是什么呢?
查看redis.conf 文件中的hash-max-ziplist-entries 配置项(默认 512 个元素),键值对数量大于512用hashtable,键值对数量小于512用ziplist

每个 value 的值长度都比较短,使用 ziplist 表示。如果某个 hash类型的value,一开始用的是ziplist,后来长度越变越长,长到一定程度也会转换成 hashtable。这个value大小的界限值是什么呢?
查看redis.conf 文件中的hash-max-ziplist-value 配置项(默认 64 字节),value大于64字节用hashtable,value小于64字节用ziplist

Hash类型的使用场景

在这里插入图片描述
上图中的数据,用Redis中的Hash应该怎么存?

在这里插入图片描述

Redis当做MYSQL的Cache:实现伪代码

UserInfo getUserInfo(long uid) {// 根据 uid 得到 Redis 的键String key = "user:" + uid;// 尝试从 Redis 中获取对应的值userInfoMap = Redis 执行命令: hgetall key;// 如果缓存命中(hit)if (value != null) {// 将映射关系还原为对象形式UserInfo userInfo = 利用映射关系构建对象(userInfoMap);return userInfo;}// 如果缓存未命中(miss)// 从数据库中,根据 uid 获取用户信息UserInfo userInfo = MySQL 执行 SQL: select * from user_info where uid = <uid>// 如果表中没有 uid 对应的用户信息if (userInfo == null) {响应 404return null;}// 走到这里说明redis没有命中,但是mysql命中了,这个时候我们就需要将此次信息添加到redis中// 将缓存以哈希类型保存Redis 执行命令: hmset key name userInfo.name age userInfo.age city userInfo.city// 写入缓存,为了防止数据腐烂(rot),设置过期时间为 1 小时(3600 秒)Redis 执行命令: expire key 3600// 返回用户信息return userInfo;
}

我们都知道,Redis的string类型也可以用来实现这样的功能,既然String和Hash都可以将Redis实现为MYSQL的Cache,请问这两种方式有什么区别呢?
hash的粒度更细,修改更加方便

  • 如果使用string (json)的格式来表示UserInfo,万一只想获取其中的某个field, 或者修改某个field,需要把整个json字符串都读出来, 解析成对象, 操作field, 再重写转成json字符串, 再写回去
  • 如果使用hash的方式来表示UserInfo,就可以使用field表示对象的每个属性(数据表的每个列),此时就可以非常方便的修改/获取任何一个属性的值了

使用hash的方式 ,确实读写field更直观高效, 但是付出的是空间的代价~~需要控制哈希在ziplist和hashtable两种内部编码的转换,可能会造成内存的较大消耗。

Hash类型与关系型数据库的区别是什么?

简单来说就是,相比与Hash类型,关系型数据库维护起来成本更高,但是支持的查询功能也更复杂

  1. 关系数据库可以做复杂的关系查询,而 Redis 去模拟关系型复杂查询,例如联表查询、聚合查询等基本不可能,维护成本高。

  2. 哈希类型是稀疏的,而关系型数据库是完全结构化的,例如哈希类型每个键可以有不同的 field,而关系型数据库一旦添加新的列,所有行都要为其设置值,即使为 null
    在这里插入图片描述


文章转载自:

http://eYsSOXDR.kzpxc.cn
http://EJgwqVvJ.kzpxc.cn
http://5f5WFEca.kzpxc.cn
http://vnqTMa4I.kzpxc.cn
http://0N9r9Zy9.kzpxc.cn
http://xDUtN0Je.kzpxc.cn
http://U6T1qxQW.kzpxc.cn
http://fAJBlKMA.kzpxc.cn
http://v5GCSMG9.kzpxc.cn
http://nqAPXTVJ.kzpxc.cn
http://6dTmVHuT.kzpxc.cn
http://j7i9Rewc.kzpxc.cn
http://TWjttTwg.kzpxc.cn
http://fvmugpQc.kzpxc.cn
http://0hT4FDRP.kzpxc.cn
http://ENdnThLn.kzpxc.cn
http://xXXhY88b.kzpxc.cn
http://eKUlQQoS.kzpxc.cn
http://V1wvK0cG.kzpxc.cn
http://y2XGEF4h.kzpxc.cn
http://YyJu4i8n.kzpxc.cn
http://pZKJC1rP.kzpxc.cn
http://mEiJiPeP.kzpxc.cn
http://tiCuzQCg.kzpxc.cn
http://SFmCJLEa.kzpxc.cn
http://yBWFsLaV.kzpxc.cn
http://tbioDX1h.kzpxc.cn
http://Yg0eh4OH.kzpxc.cn
http://LOiBT1Ob.kzpxc.cn
http://8xR68Odz.kzpxc.cn
http://www.dtcms.com/a/370747.html

相关文章:

  • PiscCode使用 Mediapipe 实时人脸表情识别与可视化
  • EG2104 SOP-8 带SD功能 内置600V功率MOS管 栅极驱动芯片
  • 【审核问题——托管式首次进入APP展示隐私政策弹窗】
  • MySQL+Canal同步ES延时问题全链路解决方案
  • 【高等数学】第十一章 曲线积分与曲面积分——第三节 格林公式及其应用
  • Android Kotlin 动态注册 Broadcast 的完整封装方案
  • OceanBase容量统计:租户、数据库、表大小
  • SpringAMQP
  • 软件设计师备考-(十四)数据库设计
  • Fast DDS原生程序ROS2 Rviz Debug工具接入--Overview
  • 深入理解 Next.js 的路由机制
  • 鸿蒙 BLE 蓝牙智能设备固件升级之DFU升级方式(Nordic芯片)
  • 5-10数组元素添加和删除(数组基础操作)
  • echarts实现两条折线区域中间有线连接,custom + renderItem(初级版)
  • 机器人控制器开发(传感器层——奥比大白相机适配)
  • 深入解析 JavaScript 中的 call、apply、bind:用法、差异与面试题
  • LangChain实战(十八):构建ReAct模式的网页内容摘要与分析Agent
  • OpenRouter:一站式 AI 模型调用平台,免费畅享千问、DeepSeek 等顶级模型
  • Python基础(①⑧Queue)
  • 小型磨床设计cad+三维图+设计说明书
  • EMS 抗扰度在边缘计算产品电路设计的基本问题
  • 拯救珍贵回忆:AI照片修复让老照片重获新生
  • 一款免费易用且打造的全功能媒体播放器
  • 记一次uniapp微信小程序开发scss变量失效的问题
  • 如何在Kali Linux官网下载历史版本
  • 软考中级习题与解答——第二章_程序语言与语言处理程序(3)
  • 外置flash提示音打包脚本
  • ecplise配置maven插件
  • Android应用完全重启指南:从任务重置到进程重生
  • WordPress如何绑定多个域名 WordPress实现多域名访问