标题:【Redis03】Redis基础:Hash相关操作
Redis基础学习:Hash相关操作
今天我们继续学习基础的数据类型 Hash 。其实从我的理解来看,这个 Hash 类型就可以看做是一个数据对象,也就是只有属性的对象,或者说是 Java 中的贫血模型对象或者 HashMap ,以及我们 PHP 中的对象属性或者键值数组。相信大家也能看出来了,如果是这种纯数据的对象属性保存,用 Hash 非常方便。不过这个类型也是非常常用的类型了,相信大家的经验也会比我多,那么我们就来再次夯实基础,学习下它的基本操作命令吧。
添加获取
添加和获取操作就是非常简单的 HSET 和 HGET ,HSET 指定一个 key ,后面接着就是 field 字段名 和 value 值,而 HGET 就是通过 key 和 field 来获取数据。
HSET key field value [field value ...]
HGET key field
127.0.0.1:6379> hset a name tome
(integer) 1
127.0.0.1:6379> hset a name tom
(integer) 0
127.0.0.1:6379> hget a name
"tom"
127.0.0.1:6379> hset a age 18
(integer) 1
127.0.0.1:6379> hset a age 20
(integer) 0
127.0.0.1:6379> hget a name
"tom"
127.0.0.1:6379> hget a age
"20"
批量操作字段
相信你也发现了,上面的 HSET 的命令参数直接就可以指定多个 field ,不过其实还有另一个命令是专门用于批量添加的。
127.0.0.1:6379> hmset c name lily age 23
OK
127.0.0.1:6379> hgetall c
1) "name"
2) "lily"
3) "age"
4) "23"
不过,HMSET 这个命令已经是被标记为过时的了,大家可以放心的直接使用 HSET ,同时要注意,这两种批量添加的操作的返回结果是不一样的。HMSET 返回的是 OK ,而 HSET 同返回的是操作成功的字段数量。
127.0.0.1:6379> hset b name jack age 22
(integer) 2
127.0.0.1:6379> hgetall b
1) "name"
2) "jack"
3) "age"
4) "22"
HGETALL 用于返回一个 Hash 中全部的字段和值。当然也有专门的命令可以指定返回哪些字段,比如 HMGET 这个命令。
127.0.0.1:6379> hmget a name age address
1) "tom"
2) "20"
3) (nil)
使用 HMGET 返回的是不带 field 的,纯 value 的数据,同时,如果指定了在 Hash 中不存在的字段名,那么会返回一个 nil 空。
HSETNX
经过前面两篇文章的学习,相信大家一看到带 NX 的就知道是什么意思啦。如果指定的 field 存在,就不能设置,或者说不能去更新这个字段的值,只有不存在 field 的情况下,才能正常操作成功。
127.0.0.1:6379> hset a address 长沙市
(integer) 0
127.0.0.1:6379> hsetnx a address 岳阳市
(integer) 0
127.0.0.1:6379> hsetnx a address2 岳阳市
(integer) 1
127.0.0.1:6379> hgetall a
1) "name"
2) "tom"
3) "age"
4) "20"
5) "address"
6) "\xe9\x95\xbf\xe6\xb2\x99\xe5\xb8\x82"
7) "address2"
8) "\xe5\xb2\xb3\xe9\x98\xb3\xe5\xb8\x82"
其它操作
基本的 Hash 操作就是上面那几个命令,非常简单也非常好用。另外还有一些辅助的命令,我们也一并学习下。
是否存在指定字段
127.0.0.1:6379> hexists a name
(integer) 1
127.0.0.1:6379> hexists a cardid
(integer) 0
删除
127.0.0.1:6379> hset a address 长沙市
(integer) 1
127.0.0.1:6379> hgetall a
1) "name"
2) "tom"
3) "age"
4) "20"
5) "address"
6) "\xe9\x95\xbf\xe6\xb2\x99\xe5\xb8\x82"
127.0.0.1:6379> hdel a address
(integer) 1
127.0.0.1:6379> hgetall a
1) "name"
2) "tom"
3) "age"
4) "20"
删除操作很好理解,那么我们可以把 Hash 中所有的 field 都删除吗?当然没问题。
127.0.0.1:6379> hdel b name
(integer) 1
127.0.0.1:6379> hdel b age
(integer) 1
127.0.0.1:6379> hgetall b
(empty array)
127.0.0.1:6379> type b
none
127.0.0.1:6379> ttl b
(integer) -2
如果我们把一个 Hash 中所有的 field 都删除了,那么这个 key 也相当于被删除了。
增减操作
127.0.0.1:6379> hincrby a age 1
(integer) 21
127.0.0.1:6379> hincrby a age -2
(integer) 19
127.0.0.1:6379> hincrbyfloat a age 3.5
"22.5"
127.0.0.1:6379> hincrbyfloat a age -1.5
"21"
这个和之前我们学习过的 INCRBY 是一样的,就是前面加了个 H ,另外在 Hash 中没有 DECRBY 相关的命令,直接使用负数就好啦。
字段数量与值长度
127.0.0.1:6379> hlen a
(integer) 4
127.0.0.1:6379> hstrlen a name
(integer) 3
这里需要注意的是,HLEN 返回的是 field 的数量,也就是我们定义的字段的数量。而 HSTRLEN 则是返回指定的某个字段内容的字符长度。
获得所有的字段或者值
127.0.0.1:6379> hkeys a
1) "name"
2) "age"
3) "address"
4) "address2"
127.0.0.1:6379> hvals a
1) "tom"
2) "21"
3) "\xe9\x95\xbf\xe6\xb2\x99\xe5\xb8\x82"
4) "\xe5\xb2\xb3\xe9\x98\xb3\xe5\xb8\x82"
很简单的两个命令,HKEYS 返回所有的 field 信息,而 HVALS 返回的则是所有值的信息。类似于 PHP 中的 array_keys() 和 array_values() 。
随机获取字段
随机获取字段是 6.2 新出的一个命令,它的参数签名如下:
HRANDFIELD key [count [WITHVALUES]]
后面的这个 count 是什么意思呢?如果不指定,就随机返回一个 field ,如果指定了,就按数量返回。如果指定的数量大于整个 Hash 中所有 field 的数量,那么就返回全部的。
127.0.0.1:6379> hrandfield a
"age"
127.0.0.1:6379> hrandfield a
"name"
127.0.0.1:6379> hrandfield a 2
1) "age"
2) "address2"
127.0.0.1:6379> hrandfield a 10
1) "name"
2) "age"
3) "address"
4) "address2"
除了正常的给一个正数的 count 之外,我们还可以给一个负值。如果是负值的话,返回的内容会有些不同,它会按照我们给出的数量,返回指定数量的 field ,并且,允许出现重复内容。
127.0.0.1:6379> hrandfield a -10
1) "age"
2) "age"
3) "address"
4) "age"
5) "age"
6) "age"
7) "address"
8) "address2"
9) "address"
10) "age"
最后还有一个 WITHVALUES 参数,它的意思是将对应的值也返回回来。
127.0.0.1:6379> hrandfield a -10 WITHVALUES
1) "age"
2) "21"
3) "name"
4) "tom"
5) "name"
6) "tom"
7) "age"
8) "21"
9) "address"
10) "\xe9\x95\xbf\xe6\xb2\x99\xe5\xb8\x82"
11) "address"
12) "\xe9\x95\xbf\xe6\xb2\x99\xe5\xb8\x82"
13) "name"
14) "tom"
15) "age"
16) "21"
17) "age"
18) "21"
19) "name"
20) "tom"
增量迭代
HSCAN 命令可以对 Hash 进行游标式的增量迭代查询,在官方文档中有这么一个说法。
命令返回的元素数量总是符合一定规则的, 对于一个大数据集来说, 增量式迭代命令每次最多可能会返回数十个元素;而对于一个足够小的数据集来说, 如果这个数据集的底层表示为编码数据结构(小的sets, hashes and sorted sets), 那么增量迭代命令将在一次调用中返回数据集中的所有元素。
所以我们需要构建一个非常大的 Hash ,如果量比较小的话是看不出效果的,它会直接将所有的内容返回,那么我们就用 PHP 来建一个。
<?php
for ($i = 0; $i <= 100000; $i++) {
$data['k' . $i] = $i;
}
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->hMSet('c', $data);
然后对这个 Hash 进行测试。
127.0.0.1:6379> hscan d 0
1) "86016"
2) 1) "k72092"
2) "72092"
3) "k12288"
4) "12288"
5) "k63131"
6) "63131"
7) "k52873"
8) "52873"
9) "k54468"
10) "54468"
11) "k16895"
12) "16895"
13) "k51935"
14) "51935"
15) "k66298"
16) "66298"
17) "k26723"
18) "26723"
19) "k65523"
20) "65523"
127.0.0.1:6379> hscan d 86016
1) "116736"
2) 1) "k67890"
2) "67890"
3) "k58599"
4) "58599"
5) "k12540"
6) "12540"
7) "k7451"
8) "7451"
9) "k34905"
10) "34905"
11) "k48765"
12) "48765"
13) "k2167"
14) "2167"
15) "k77963"
16) "77963"
17) "k20956"
18) "20956"
19) "k90465"
20) "90465"
可以看到,第一次查询之后,返回的游标是 86016 ,第二次我们就从这个游标继续,直到游标返回 0 就表示所有的数据都被迭代完成了。另外我们还可以指定两个参数。
127.0.0.1:6379> hscan d 0 match k1*
1) "86016"
2) 1) "k12288"
2) "12288"
3) "k16895"
4) "16895"
127.0.0.1:6379> hscan d 86016 match k1*
1) "116736"
2) 1) "k12540"
2) "12540"
127.0.0.1:6379> hscan d 0 match k1* count 1
1) "49152"
2) 1) "k12288"
2) "12288"
MATCH 是可以指定匹配模式的,比如上面我们匹配查询到的结果中只包含有 k1 开头的数据。另外一个 COUNT 则是指定返回结果的数量。
HSCAN 和 SCAN 命令是一样的,后期我们还会再学习到 SCAN 命令相关的操作,大家可以提前先了解下。为什么要使用 HSCAN 这种命令呢?其实这就是底层原理的问题,Redis 是单线程应用,同一时间只会有一个命令在处理,如果一个 Hash 非常大,那么在使用 HGETALL 的时候就会阻塞整个线程半天,类似于在线上用 keys * 的效果。而 HSCAN 则可以渐进式的获取,不会产生长时间的阻塞。
总结
Hash 这个数据类型的操作其实不难,命令也不多,但是就像开头所说的,对于一个数据对象的保存来说这个结构非常合适。如果不使用它的话,我们可能需要将一个对象序列化成 JSON 或者是 PHP 自带的序列化字符串,才能使用 Redis 的 String 类型进行保存。而有了这个 Hash 之后,就能够非常方便地保存和获取需要的数据对象了。
视频链接
微信文章地址:https://mp.weixin.qq.com/s/tVOHLyfv2CcywdozCeuxiA