Redis的数据结构之Hash

364 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情

简介

Redis的Hash是一种键值对(key/value)的数据结构,其中value可以为多个(filed/value)的集合,便于对象存储。

底层数据结构

Redis的哈希对象的底层存储可以使用ziplist和hashtable。当hash对象可以同时满足一下两个条件时,哈希对象使用ziplist编码存储。

  • 哈希对象保存的所有键值对的键和值的字符串长度都小于64字节
  • 哈希对象保存的键值对数量小于512个

ziplist

关于ziplist(压缩列表)的数据结构,在上一章讲解list的数据结构时进行了相信的讲解,本章不在重复说明。

hashtable

redis的hashtable结构与java中的hashtable结构非常的类似都是采用数组加链表的形式具体结构如下:

图片.png

说明:hashtable属性是一个数组,数组中的每个元素都是一个指向dictEntry结构的指针,每个dictEntry结构都保存键值对。

哈希冲突

java中hashmap的hash冲突问题,解决的方案如下:

  1. 开放定址法,也称为线性探测法,就是从发生冲突的那个位置开始,按照一定的次序从hash表中找到一个空闲的位置,然后把发生冲突的元素存入到这个空闲位置中。

  2. 链式寻址法,这是一种非常常见的方法,简单理解就是把存在hash冲突的key,以单向链表的方式来存储,比如HashMap就是采用链式寻址法来实现的。

  3. 再hash法,就是当通过某个hash函数计算的key存在冲突时,再用另外一个hash函数对这个key做hash,一直运算直到不再产生冲突。这种方式会增加计算时间,性能影响较大

  4. 建立公共溢出区, 就是把hash表分为基本表和溢出表两个部分,凡事存在冲突的元素,一律放入到溢出表中。

Redis中的hashtable的hash冲突问题

解决方案:采用链地址法来解决哈希冲突,每个节点有一个next指针,新的键值对冲突时,则将新的键值对dictEntry放在单链表的首部。

REHASH

随着时间的推移,hash表中键值对有可能会逐渐增多或是较少。随着时间的推移,hash表中键值对有可能会逐渐增多或是较少。让redis的负载因子,维持在一个合理的范围,有利于系统更好的管理内存资源。键值对过多或者过少的,就需要对哈希表进行动态扩容或者收缩,而这些操作都是通过rehash重新散列来实现的。

渐进式rhash

当hash表中的键值减少的情况下,rehash可以短时间内一次性完成,如果键值存上的数量为百万或者千万的级别,那么将会采用分批式的渐进式的rehash,这样技能保证服务不中断,也能使得业务不受影响。

常用命令

图片.png

hset设置hash的值

图片.png

hget 获取存储在哈希表中指定字段的值。

图片.png

hgetall 获取在哈希表中指定 key 的所有字段和值

图片.png

hlen 获取哈希表中字段的数量

图片.png

hdel 删除一个或多个哈希表字段

图片.png

hexists 查看哈希表的指定字段是否存在。

图片.png

Hash与String的存储区别

1.hash中存储每个对象的属性,查询单个属性的值不需要解析JSON速度较快,如果要查询对象的全部字段会比较慢。嵌套类型的对象(即对象里面还包着对象)无法轻易存储

2.String正好相反,因为数据存储是JSON而JSON解析非常快,尤其是一次性查询很多个字段的时候,但是如果只查询一个字段,速度就比较慢。

Hash应用场景

1.购物车

以用户id为key,商品id为field,商品数量为value,进行存储。

2.记录帖子的点赞数、评论数和点击数。

以帖子为key,点赞数、评论数和点击数为属性进行存储。

总结

本文讲解了Redis的Hash的数据结构、常规的操作命令,以及相对应的应用场景,我们需要针对业务的需求选择合适的Redis的数据结构进行存储。