前言
redis 中所有的 key 都是字符串,value 的类型是存在差异的,因此出现了操控不同 value 的命令,接下来,就一起来学习一下吧~
Ps1:接下来,我给出的指令都是按照 Redis 官方文档的语法格式来解析的,[ ] 相当于一个独立的单元,表示可选项(可有可无),其中 | 表示 “或者” 的意思,多个只能出现一个,[ ] 和 [ ] 之间是可以同时存在的.
Ps2:一个快速失去年终奖的小技巧 —— 清除 redis 上所有的数据 =》 FLUSHALL,这个操作可以把 redis 上所有的键值对全部带走.
一、hash 类型
1.1、操作命令
哈希表可以说是所有数据结构中最重要的,既是日常开发中最常用的,也是面试考察的重点.
Redis 自身已经是键值对结构了,但 value 实际上还是一些个键值对(哈希表)
hset / hget(设置 / 获取)
设置 hash 中指定的字段 field 和 value ,返回值是设置成功的键值对(field - value)的个数.
HSET key field value [field value ...]
获取 hash 中指定字段 field 的 value 值.
HGET key field
hexists(检查是否存在)
hexists 用来 hash 中检查字段 field 是否存在,返回 1 表示存在,返回 0 表示不存在.
HEXISTS key field
hdel(删除)
hdel 用来删除 hash 中指定的字段,返回值是本次操作删除的字段个数.
HDEL key field [field ...]
Ps:del 删除的是 key,hdel 删除的是 field
hkeys(获取所有 field)
hkeys 用来获取指定 hash 的所有 field,返回值是所有的 field ,操作的时间复杂度为 O(N),此处 N 为 hash 的元素个数.
HKEYS key
Ps:这个操作也是存在一定的风险的,类似与之前介绍过的 keys *,主要是咱也不知道某个 hash 中是否存在大量的 field.
hvals (获取所有的 value)
和 hkeys 相对,能够获取到 hash 中所有的 value,操作的时间复杂度也是 O(N),N 是 哈希 元素的个数,如果 哈希 非常大,也可能导致 redis 服务器阻塞.
HVALS key
hgetall(获取所有键值对)
获取指定 hash 中所有的键值对,一般不要在生产环境使用这个命令,因为你不知道这个 hash 中有多少键值对,就可能导致 redis 阻塞,最后带走你的年终奖~
HGETALL key
hmget(查询指定个 value)
通过 hash 可以根据指定个 field 查出相应的 value,类似于之前学习到的 mget .
HMGET key field [field ...]
hlen(查询个数)
用来查询 hash 的元素个数,这个操作不需要遍历,时间复杂度是 O(1).
HLEN key
hsetnx(设置值时设置参数)
类似于 setnx,当 hash 的 field 不存在时,才能设置成功,若存在则不行.
返回 1 表示成功,返回 0 表示失败.
HSETNX key field value
hash 自增自减处理
hash 这里的 value 也可以当作数字来处理:
- hincrby:可以加减整数.
- hincrbyfloat:可以加减小数.
这些命令不经常使用,因此没有提供 incr decr... 这类命令.
1.2、hash 的内部编码方式
hash 内部有两种编码方式:
- hashtable:最基本的哈希表(不是 java 标准库中的 HashTable),redis 内部对哈希表的实现方式和 java 中的哈希表可能不太一样,但是整体思想都是一样的;时间上复杂度O(1),但是空间上会有一定的浪费(hash 是一个数组,数组上有些位置有元素,有些位置没有元素).
- ziplist:压缩列表,当哈希表里的元素比较少的时候,就优化成了 ziplist 了,能够节省空间,但是读写元素的速度比较慢.
ziplist 到底时怎么压缩的?
压缩的本质就是对数据进行重新编码,不同的数据有不同的特点,结合这些特点,重新编码后,就能够缩小体积~
例如 abbcccdddd 重新编码就变成了 1a2b3c4d.
再例如,有一个文件内容是:abcd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000efgh
重新编码后就是 abcd[100]efgh.
ziplist 也是同理~
什么时候使用 ziplist?什么时候使用 hashtable?
- 如果 hash 中的元素比较少,使用 ziplist 表示. 元素比较多的时候使用 hashtable 表示.
- 每个 value 值的长度都比较短的,使用 ziplist 表示. 如果某个 value 长度太长,也会转化成 hashtable.
为什么我这里没有提到像“39”这样的具体数值?
因为这些数据类型内部转化的值都是需要根据实际工作情况而定的,他们都是再 redis.conf 文件中可以配置,满足一下任意一个就会发生转化,如下:
- hash-max-ziplist-entries(默认 512 个):元素个数小于这个 配置值 后,转化为 ziplist.
- hash-max-ziplist-value(默认 64 字节):所有 field 对应的 val 值都小于这个 配置值 后,转化为 ziplist.
1.3、应用场景
缓存功能
hash 类型存储缓存相比于 string 类型就有更多的更合适的使用场景.
例如,我有以下这样一个 UserInfo 信息
假设这样一个场景就是:万一只想获取其中某一个 field ,或者修改某一个 field 的 val
如果使用 json 格式来表示 UserInfo ,就需要把数据组织成如下结构
set user:1:name James
set user:1:age 23
set user:1:city Beijing
就需要把整个 json 都读出来,解析成对象,操作field ,再转化成 json 字符串,再写回去.
但如果使用 hash 的方式来表示 UserInfo 就可以使用 field 表示对象的每一个属性(数据表的每一列),此时就可以很方便的获取属性的值了,如下结构
这就相当于把每个用户的各个属性给聚集起来了,实现了高内聚~
什么是高内聚,低耦合?
高内聚,就是把所有关联的东西放在一起,最好能够放在指定的位置,好找~
讲个以前的故事:我小的时候有一个缺点,就是喜欢乱放东西,衣服经常会出现在很多地方,但唯独不会出现在衣柜,因此每次我要找某一个衣服的时候就很困难,因为需要把家里的各个角落都遍历一遍.
这种场景就是属于 低内聚 ~
耦合,指的是 两个模块 / 代码 之间的关联关系,关联关系越大,越容易受影响,就认为耦合越大,而我们追求的是 低耦合 ,避免 “牵一发而动全身”,某一个地方出一个 bug,影响到了其他地方
讲个以前的故事:之前,有一次我爸得了肾结石,疼的满地打滚,后来就我去住院了,把那个结石取出来了,当时我妈为了照顾我爸,就推掉了当时工作的所有任务,请假来医院全程照顾我妈.
这种场景就输入 高耦合.
另外,当时在我微信里有一个不太熟悉的朋友(指网上认识的~)后来他发了个朋友圈,也是生病住院了,但我这边是不会有任何影响的,甚至还会给他的朋友圈点个赞 ~
这种场景就属于 低耦合.