持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情
简介
Redis的Hash是一种键值对(key/value)的数据结构,其中value可以为多个(filed/value)的集合,便于对象存储。
底层数据结构
Redis的哈希对象的底层存储可以使用ziplist和hashtable。当hash对象可以同时满足一下两个条件时,哈希对象使用ziplist编码存储。
- 哈希对象保存的所有键值对的键和值的字符串长度都小于64字节
- 哈希对象保存的键值对数量小于512个
ziplist
关于ziplist(压缩列表)的数据结构,在上一章讲解list的数据结构时进行了相信的讲解,本章不在重复说明。
hashtable
redis的hashtable结构与java中的hashtable结构非常的类似都是采用数组加链表的形式具体结构如下:
说明:hashtable属性是一个数组,数组中的每个元素都是一个指向dictEntry结构的指针,每个dictEntry结构都保存键值对。
哈希冲突
java中hashmap的hash冲突问题,解决的方案如下:
-
开放定址法,也称为线性探测法,就是从发生冲突的那个位置开始,按照一定的次序从hash表中找到一个空闲的位置,然后把发生冲突的元素存入到这个空闲位置中。
-
链式寻址法,这是一种非常常见的方法,简单理解就是把存在hash冲突的key,以单向链表的方式来存储,比如HashMap就是采用链式寻址法来实现的。
-
再hash法,就是当通过某个hash函数计算的key存在冲突时,再用另外一个hash函数对这个key做hash,一直运算直到不再产生冲突。这种方式会增加计算时间,性能影响较大
-
建立公共溢出区, 就是把hash表分为基本表和溢出表两个部分,凡事存在冲突的元素,一律放入到溢出表中。
Redis中的hashtable的hash冲突问题
解决方案:采用链地址法来解决哈希冲突,每个节点有一个next指针,新的键值对冲突时,则将新的键值对dictEntry放在单链表的首部。
REHASH
随着时间的推移,hash表中键值对有可能会逐渐增多或是较少。随着时间的推移,hash表中键值对有可能会逐渐增多或是较少。让redis的负载因子,维持在一个合理的范围,有利于系统更好的管理内存资源。键值对过多或者过少的,就需要对哈希表进行动态扩容或者收缩,而这些操作都是通过rehash重新散列来实现的。
渐进式rhash
当hash表中的键值减少的情况下,rehash可以短时间内一次性完成,如果键值存上的数量为百万或者千万的级别,那么将会采用分批式的渐进式的rehash,这样技能保证服务不中断,也能使得业务不受影响。
常用命令
hset设置hash的值
hget 获取存储在哈希表中指定字段的值。
hgetall 获取在哈希表中指定 key 的所有字段和值
hlen 获取哈希表中字段的数量
hdel 删除一个或多个哈希表字段
hexists 查看哈希表的指定字段是否存在。
Hash与String的存储区别
1.hash中存储每个对象的属性,查询单个属性的值不需要解析JSON速度较快,如果要查询对象的全部字段会比较慢。嵌套类型的对象(即对象里面还包着对象)无法轻易存储
2.String正好相反,因为数据存储是JSON而JSON解析非常快,尤其是一次性查询很多个字段的时候,但是如果只查询一个字段,速度就比较慢。
Hash应用场景
1.购物车
以用户id为key,商品id为field,商品数量为value,进行存储。
2.记录帖子的点赞数、评论数和点击数。
以帖子为key,点赞数、评论数和点击数为属性进行存储。
总结
本文讲解了Redis的Hash的数据结构、常规的操作命令,以及相对应的应用场景,我们需要针对业务的需求选择合适的Redis的数据结构进行存储。