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

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://www.xdnf.cn/news/20249.html

相关文章:

  • 【数据结构】带哨兵位双向循环链表
  • 50系显卡训练深度学习YOLO等算法报错的解决方法
  • 《动手学深度学习v2》学习笔记 | 2.4 微积分 2.5 自动微分
  • 深度学习——PyTorch保存模型与调用模型
  • JUC之并发编程
  • MyBatis入门到精通:CRUD实战指南
  • 使用UniApp实现下拉框和表格组件页面
  • Android Kotlin 动态注册 Broadcast 的完整封装方案
  • uv教程 虚拟环境
  • kotlin - 2个Fragment实现左右显示,左边列表,右边详情,平板横、竖屏切换
  • 【LeetCode 每日一题】2348. 全 0 子数组的数目
  • 开源OpenHarmony润开鸿HH-SCDAYU800A开发板开箱体验
  • AI热点周报(8.31~9.6): Qwen3‑Max‑Preview上线、GLM-4.5提供一键迁移、Gemini for Home,AI风向何在?
  • C++进阶——继承(2)
  • 基于STM32的交通灯设计—紧急模式、可调时间
  • 如何理解`(line_status = parse_line()) == LINE_OK`?
  • @Autowired注解(二)
  • 【CAN通信】AUTOSAR架构下TC3xx芯片是如何将一帧CAN报文接收上来的
  • Xsens解码人形机器人训练的语言
  • 如何通过AI进行数据资产梳理
  • 43这周打卡——生成手势图像 (可控制生成)
  • 球坐标系下调和函数的构造:多项式边界条件的求解方法
  • linux Nginx服务配置介绍,和配置流程
  • 快手Keye-VL 1.5开源128K上下文+0.1秒级视频定位+跨模态推理,引领视频理解新标杆
  • 错误是ModuleNotFoundError: No module named ‘pip‘解决“找不到 pip”
  • vsan default storage policy 具体是什么策略?
  • HTB GoodGames
  • centos下gdb调试python的core文件
  • 串口通信的学习
  • 日内5%,总回撤10%:EagleTrader风控规则里,隐藏着什么核心考点?