Redis_7_hash

哈希

哈希表是我们在Java学习中是最最重要的数据结构。

原因有2个:

1、日常开发中,哈希表的出场频率非常高。

2、面试中,哈希表也是非常重要的考点。


Redis自身已经是键值对结构了,Redis自身的键值对就是通过哈希的方式来组织的。把key这一层组织完成之后,到了value这一层~~value的其中一种类型还可以再是哈希。

命令

hset

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

复制代码
HSET key field value [field value ...]

这里的value是以字符串的形式进行存储的。

时间复杂度:插入一组field为O(1),插入N组field为O(N)

返回值:添加字段的个数。

hget

获取hash中指定字段的值。

复制代码
HGET key field

时间复杂度:O(1)

返回值:字段对应的值或者nil。

hexists

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

复制代码
HEXISTS key field

时间复杂度:O(1)。

返回值:1表示存在,0表示不存在。

hdel

删除hash中指定的字段。

复制代码
1 HDEL key field [field ...]

时间复杂度:删除一个元素为O(1).删除N个元素为O(N)。

返回值:本次操作删除的字段个数。

del删除的是key,hdel删除的是value。


hkeys

获取hash中的所有字段。

复制代码
HKEYS key

时间复杂度:这个操作,先根据key找到对应的hash[ O(1)],然后遍历hash[O(N)这里的N是hash的元素个数]。总体是O(n)[n为hash中元素个数]。

在redis中谈到O(N),N表示:

1、redis整体的key的个数

2、当前命令中key的个数(可近似看作O(1))

3、当前key对应的value里面的元素个数

......

返回值:字段列表

注意:这个操作也是存在一定风险的!!!类似于之前介绍过的keys *!!!

主要是咱们也不字段某个hash是否会存在大量的field~~

hvals

获取hash中所有的value。(和hkeys相对)

复制代码
HVALS key

时间复杂度:这个操作,先根据key找到对应的hash[ O(1)],然后遍历hash[O(N)这里的N是hash的元素个数]。总体是O(n)[n为hash中元素个数]。

返回值:所有的值。

和hkeys一样,如果哈希非常大,这个操作就可能倒是redis服务器被阻塞住。

H系列的命令,必须保证key对应的value得是哈希类型!!!

hgetall

获取hash中所有的键值对。

复制代码
HGETALL key

时间复杂度:这个操作,先根据key找到对应的hash[ O(1)],然后遍历hash[O(N)这里的N是hash的元素个数]。总体是O(n)[n为hash中元素个数]。

返回值:字段和对应的值。

注意:像上面这三个操作都是比较危险的,因为在生产环境中,你不知道当前hash里面有多少键值对,从而导致执行耗时较长,从而阻塞redis!!!

如果需要遍历生产环境中的hash,可以使用hscan来进行遍历redis的hash,它属于"渐进式遍历",敲一次,遍历一小部分,再敲一次,遍历一小部分......连续执行多次,就可以遍历完整个表了。这种思想也被称为化整为零。Java中的ConcurrentHashMap也是使用的这个思想,这个哈希表在扩容的时候,类似于蚂蚁搬家,一次搬运一小部分......


多数情况下,是不需要查询所有的field,只要查询其中的几个field即可~

hmget

类似于之前的mget可以一次查询多个field。

复制代码
HMGET key field [field ...]

时间复杂度:只查询一个元素为O(1),查询多个元素为O(N),N为查询元素个数。

返回值:字段对应值或者nil。

有没有hmset设置多个field的value呢?有,但是没必要使用,hset本身已经支持设置多个field和value了。

hlen

获取hash中所有字段的个数。

复制代码
HLEN key

时间复杂度:O(1),这里获取长度不需要遍历哈希表,只需要用一个变量存储哈希表中字段个数即可。

返回值:字段个数。

hsetnx

类似于setnx 不存在的时候才能设置成功,如果存在,则失败。

时间复杂度:O(1)

返回值:1表示设置成功,0表示失败。

hincrby

将hash中字段对应的数值添加指定的值

复制代码
HINCRBY key field increment

时间复杂度:O(1)

返回值:该字段变化后的值。

hincrbyfloat

hincrby的浮点数版本。

复制代码
HINCRBYFLOAT key field increment

时间复杂度:O(1)

返回值:该字段变化之后的值。

总结

哈希编码方式

哈希内部的编码方式有两种:

1、哈希中的元素个数比较少,使用ziplist表示,元素个数比较多,使用hashtable来表示。

2、每个value的值长度都比较短,使用ziplist表示,如果某个value长度太长了,也会转换成hashtable。

下图中的配置是写到redis.conf中的,可以修改的~

ziplist是一个压缩列表,那么什么是压缩呢?

在我们的计算机中,经常有一些文件的后缀是:rar,zip,gzip,7z......一些具体的压缩算法~~压缩的本质,是针对数据进行重新编码。

不同的数据,有不同的特点,结合这些特点,进行精妙的设计。重新编码之后,就能够缩小体积~~

例如:

ziplist也是同理~~内部的数据结构也是精心设计的,目的是为了节省内存空间。

在元素少的时候,表示一个普通的hash表可能会浪费一定的空间~~(hash首先是一个数组,数组上有些位置有元素,有些没有元素)。

ziplist付出的代价,进行读写元素,速度是比较慢的。如果元素个数少,慢的并不明显。如果元素个数太多了,慢就会雪上加霜。

哈希的应用场景

1、作为缓存

在redis中,string是可以作为缓存使用的,但是存储结构化数据,使用hash类型更合适一些~~

上述场景使用string类型也能够做到,但是就需要使用到json这样的数据格式了。

如果使用json格式来表示UserInfo万一指向获取其中某个field,或者修改某个field,就需要把整个json读出来解析成对象,操作field,再重新写成json字符串,再写回去~~

如果使用hash的方式来表示UserInfo。就可以使用field表示对象的每个属性(数据表的每个列),此时就可以非常方便地修改/获取任何一个属性地值了~~

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

哈希类型和关系型数据库两点不同之处:

1、哈希类型是稀疏的,而关系型数据库是完全结构化的,例如:哈希类型每个键可以有不同的field,而关系型数据库一旦添加新的列,所有行都要为其设置值,即使为null。

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

缓存类型对比:

1、原生字符串类型

优点:实现简单,针对个别属性变更也很灵活。

缺点:低内聚(相同的模块,联系却很少),实用性低

2、序列化字符串类型


优点:针对总是以整体作为操作的信息比较合适,编程也简单。同时,如果序列化方案选择合适,内 存的使用效率很高。

缺点:每次操作都需要进行对象和json字符串之间格式转换。

3、哈希类型

优点:简单、直观、灵活

缺点:需要消耗更大的内存空间,

相关推荐
dudke2 小时前
c#实现redis的调用与基础类
数据库·redis·缓存
许愿OvO2 小时前
MySQL-索引
数据库·mysql
-指短琴长-2 小时前
MySQL快速入门——基本查询(上)
android·数据库·mysql
Yeats_Liao3 小时前
时序数据库系列(四):InfluxQL查询语言详解
数据库·后端·sql·时序数据库
白衣鸽子3 小时前
MySQL数据库的“隐形杀手”:深入理解文件结构与治理数据碎片
数据库·后端·mysql
IvanCodes3 小时前
openGauss安装部署详细教程
大数据·数据库·sql·opengauss
王道长服务器 | 亚马逊云3 小时前
AWS + 苹果CMS:影视站建站的高效组合方案
服务器·数据库·搜索引擎·设计模式·云计算·aws
java干货3 小时前
MySQL “灵异事件”:我 INSERT id=11,为什么被 UPDATE id=10 锁住了?
android·数据库·mysql
記億揺晃着的那天4 小时前
数据库中 ACID 四个核心特性
数据库·mysql·oracle·系统设计·acid