Redis的Hash类型
一.Hash类型结构
此处的key就是user:1,value是一个哈希
二.Hash类型的命令
1.HSET
返回设置成功的键值对(field - value)个数
演示:
2.HGET
如果redis中的key不存在则返回nil,如果key存在,value中的field不存在,也是返回nil
演示:
3.HEXISTS
演示:
4.HDEL
HDEL删除的是field,DEL删除的是redis的key
演示:
5.HKEYS
根据redis的key找到对应的哈希,再对这个哈希进行遍历
需要注意的是:
这个操作也存在一定的风险类似于之前的keys *,当key中对应的hash过多了,此时进行遍历也会造成阻塞其他redis的客户端。
演示:
6.HVALS
演示:
6.HGETALL
演示:
7.HMGET
演示:
8.HLEN
演示:
9.HSETNX
演示:
10.HINCRBY
演示:
11.HINCRBYFLOAT
演示:
三.Hash类型的编码方式
- 哈希中的元素个数比较少,使用ziplist表示,元素个数比较多,使用hashtable表示
- 每个value的值的长度都比较短,使用ziplist,如果某个value的值太长了,也会转换成hashtable
哈希中的存在两种编码方式:
- 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,使用一个普通的hash表,可能会浪费一定的空间。虽然,ziplist付出的代价是进行读写元素时,速度较慢,但是,如果元素个数比较少,慢的就不太明显,除非元素个数太多了,才会比较明显。
四.应用场景
1.作为缓存
前面提到的String也是作为缓存来进行使用的,但是存储结构化数据(类似于数据库中表的结构)时,使用hash类型作为缓存会更加合适。
使用hash作为缓存:
使用String作为缓存
- String类型也能做到hash这种存储数据的格式,但是需要使用到JSON这样的数据格式才能够像hash那样的方式进行存储数据。
缓存用户信息:
原生字符串类型,使用字符串类型,让每个属性一个key,但是这样的办法相当于把同一个数据的各个属性分散开进行表示(低内聚)
如果使用String(JSON)的格式来表示UserInfo,万一只想获取其中某个field,或者修改某个field就需要把整个JSON都读出来,解析成对象,操作完field后,再重新转成JSON字符串,再写回去。
如果使用hash的方式表示UserInfo,就可以使用field表示对象的每个属性(数据表的每个列),此时就可以非常方便的修改/获取任何一个属性的值了。虽然使用hash的方式更直观高效,但是付出的是空间代价,因为要控制哈希在ziplist和hashtable两种内部编码的转换,可能会造成内存的较大消耗。
内聚和耦合解释
内聚:内聚指一个模块(如类、函数、组件)内部各元素(代码、数据、功能)之间的关联程度。高内聚表示模块内所有元素协同完成单一、明确的任务,低内聚则意味着模块功能混杂、职责分散。
耦合:耦合指模块之间的依赖关系强度。高耦合意味着模块间依赖复杂,修改可能引发连锁反应;低耦合则通过清晰接口隔离变化,提升系统灵活性。
2.哈希类型和关系型数据库不同的两点
(1)稀疏性
哈希类型是稀疏的,⽽关系型数据库是完全结构化的,例如哈希类型每个键可以有不同的field,⽽
关系型数据库⼀旦添加新的列,所有⾏都要为其设置值,即使为null。
hash类型存储
关系型数据库存储:
(2)复杂关系查询
关系数据库可以做复杂的关系查询,⽽Redis去模拟关系型复杂查询,例如联表查询、聚合查询等基本不可能,维护成本高。