Redis Hash数据类型深度解析:从命令、原理到实战场景

前言

在Redis的众多数据结构中,Hash(哈希)类型占据着至关重要的地位。Redis本身就是一个高性能的键值(Key-Value)数据库,其底层的键值对便是通过哈希方式组织的。而Hash数据类型则更进一步,它允许在Value层级再构建一个键值对集合,形成一种"域-值"(Field-Value)的映射关系。这种结构使得Hash特别适合用来存储对象信息,您可以将一个对象的多个属性高效地聚合在一个Redis键中

之前学过的所有数据结构中,最最重要的

redis自身已经是键值对的结构了

redis自身的键值对就是通过哈希的方式来组织的额

把key这一层组织完成之后,到了value这一层,value的其中一种类型还可以是哈希

hset、hget

hset就是设置键值对

field->value

返回值是设置成功的键值对(field--value)

bash 复制代码
hset key field value [field value...]

然后我们使用hget进行key里面键值对的获取

bash 复制代码
hget key field

如果要查询的内容不存在的话就返回nil

hexists

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

bash 复制代码
hexists key field

返回1就是存在,返回0就是不存在

hdel

删除hash中指定的字段

del删除的是key

hdel删除的是field

bash 复制代码
hdel key field [field]

都是先指定key,再来指定一个或者多个field

返回值就是本次操作删除的字段个数

查看是否存在我们是查不到的,因为已经删除了

hkeys 、hvals

获取hash中的所有字段

bash 复制代码
hkeys key

返回值:字段列表

时间复杂度:O(N)

原理就是根据Key找到对应的hash O(1)

然后遍历hash O(N)这个N是hash的元素个数

这个查询到的就是我们当前hash中的所有的Key了

这个操作也是存在一定风险的,类似于之前介绍的keys *

主要是我们也不知道某个hash钟是否存在大量的field

可能会造成阻塞

bash 复制代码
hvals key

能够获取hash中所有的value

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

hgetall 、hmget

hegtall获取hash中的所有字段以及对应的值

bash 复制代码
hgetall key

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

hget只能查询一个field,但是hmget可以查询多个field

bash 复制代码
hmget key

多个value的顺序和value是匹配的

有没有hmset,一次设置多个field和value呢?

有,但是并不需要,因为hset已经支持一次设置多个field和value了

hkeys,hvals,hgetall都是存在一定风险的,hash的元素个数太多了,执行的耗时会比较长,从而阻塞redis

hscan遍历redis的hash,但是他是属于渐进式遍历,就是敲一次命令,遍历一小部分,再敲一次,再遍历一小部分

连续执行多次,就可以完成整个的遍历过程了

hlen、hsetnx

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

bash 复制代码
hlen key

这个获取hash中的某个字段的个数我们是不需要进行遍历的

hsetnx

在字段不存在的情况下,设置hash中的字段和值,和之前的setnx很相似的

bash 复制代码
hsetnx key field value

hincrby 、hincrbyfloat

hash这里的value,也可以当做数字来处理

hincrby就可以加减整数,返回计算后的结果

hincrbyfloat可以加减小数

好的,这是根据图片内容生成的表格:

命令小结

命令 执行效果 时间复杂度
hset key field value 设置值 O(1)
hget key field 获取值 O(1)
hdel key field [field ...] 删除 field O(k), k 是 field 个数
hlen key 计算 field 个数 O(1)
hgetall key 获取所有的 field-value O(k), k 是 field 个数
hmget field [field ...] 批量获取 field-value O(k), k 是 field 个数
hmset field value [field value ...] 批量获取 field-value O(k), k 是 field 个数
hexists key field 判断 field 是否存在 O(1)
hkeys key 获取所有的 field O(k), k 是 field 个数
hvals key 获取所有的 value O(k), k 是 field 个数
hsetnx key field value 设置值,但必须在 field 不存在时才能设置成功 O(1)
hincrby key field n 对应 field-value +n O(1)
hincrbyfloat key field n 对应 field-value +n O(1)
hstrlen key field 计算 value 的字符串长度 O(1)

hash内部编码

哈希的内部编码有两种:

  • ziplist(压缩列表):当哈希类型元素个数⼩于 hash-max-ziplist-entries 配置(默认 512 个)、同时所有值都⼩于 hash-max-ziplist-value 配置(默认 64 字节)时,Redis 会使⽤ ziplist 作为哈希的内部实现,ziplist 使⽤更加紧凑的结构实现多个元素的连续存储,所以在节省内存⽅⾯⽐hashtable 更加优秀。
  • hashtable(哈希表):当哈希类型⽆法满⾜ ziplist 的条件时,Redis 会使⽤ hashtable 作为哈希的内部实现,因为此时 ziplist 的读写效率会下降,⽽ hashtable 的读写时间复杂度为 O(1)。

压缩的本质是针对数据进行重新编码

ziplist付出的代价,进行读写元素,速度是比较慢的

哈希中的元素比较少,使用ziplist,元素多的话就使用hashtable

每个value的长度比较短,使用ziplist

如果太长了的话,也会转换成hashtable

使用场景

关系型数据表保存用户信息


高内聚低耦合

Redis Hash因其结构化的数据存储方式和高效的字段访问能力,在实际应用中非常广泛。

  1. 缓存对象信息:这是最经典的使用场景。例如,用户信息、商品信息、会话数据等都可以存储在Hash中。 相比于将整个对象序列化为JSON字符串再存入Redis,使用Hash可以让您独立地更新或获取对象的某个属性,而无需读取和重写整个对象,这在并发更新时能避免数据覆盖问题并提升性能。

    • 示例: 一个用户对象可以这样存储:HSET user:1001 name "Alice" age 30 email "
  2. 购物车:电商应用中的购物车功能与Hash结构完美契合。可以用用户ID作为键(key),商品ID作为域(field),商品数量作为值(value)。

    • 示例: HSET cart:1001 product:558 2 表示用户1001的购物车中有2件ID为558的商品。通过HINCRBY可以方便地增减商品数量。
  3. 计数器聚合:当需要对一个对象的多个指标进行计数时,Hash非常有用。例如,记录一篇文章的点赞数、评论数、分享数。

    • 示例: HINCRBY article:998 likes 1,HINCRBY article:998 comments 1。

总结

Redis的Hash数据类型提供了一种在单个键下存储多个键值对的高效方式,是模拟和存储对象数据的理想选择。它通过提供丰富的命令,实现了对对象属性的灵活、高效操作。相比于使用JSON字符串存储,Hash在更新部分字段时性能优势明显,且更加节省网络带宽。理解并善用ziplist和hashtable两种内部编码的特点,可以在内存效率和执行性能之间找到最佳平衡点,从而更好地发挥Redis的强大能力。