11.hash哈希散列
- Redis hash(哈希散列)是由字符类型的 field(字段)和 value 组成的哈希映射表结构(也称散列表),但 value 是一个键值对,是一个键值对集合。它非常类似于表格结构。在 hash 类型中,field 与 value 一一对应,且不允许重复。
类似于Map集合结构:Map<key,map<key,valule>>
- Redis hash 特别适合于存储对象。一个 filed/value 可以看做是表格中一条数据记录;而一个 key 可以对应多条数据。下面举一个例子,使用 hash 类型存储表格中的数据,这里以
user
为 key,id:1
为字段,name:Cao
为 value:
id | name |
---|---|
1 | wu |
2 | li |
- 命令实例演示:
#以user为key,设置 id+序号为字段,name+名字为值
remote:0>hmset user id:1 wu id:2 li
"OK"
# 查询 user 这个key下所有的数据,并以字符串的形式将值返回
remote:0>hgetall user
1) "id:1"
2) "wu"
3) "id:2"
4) "li"
注意:当我们对 value 进行查询时,这个值只能以字符串的形式返回。
- 通过上述方法,我们就把表格中的数据存储在了内存中。Redis hash 的存储结构如下图所示:
- 一个 hash 类型的 key 最多可以存储 2^32-1(约 40 亿个)字段/值。同时 Redis hash 会为这个 key 额外储存一些附加的管理信息,比如这个键的类型、最后一次访问这个键的时间等,所以 hash 键越来越多时,Redis 耗费在管理信息方面的内存就越多。当 hash 类型移除最后一个元素后,该存储结构就会被自动删除,其占用内存也会被系统回收。
11.1 hash 类型
-
hash 类型是 Redis 常用数据类型之一,其底层存储结构有两种实现方式。
-
第一种,当存储的数据量较少的时,hash 采用 ziplist 作为底层存储结构,此时要求符合以下两个条件:
- 哈希对象保存的所有键值对(键和值)的字符串长度总和小于 64 个字节。
- 哈希对象保存的键值对数量要小于 512 个。
-
当无法满足上述条件时,hash 就会采用第二种方式来存储数据,也就是 dict(字典结构),该结构类似于 Java 的 HashMap,是一个无序的字典,并采用了数组和链表相结合的方式存储数据。在 Redis 中,dict 是基于哈希表算法实现的,因此其查找性能非常高效,其时间复杂度为 O(1)。
-
哈希表又称散列表,其初衷是将数据映射到数组中的某个位置上,这样就能够通过数组下标来访问该数据,从而提高数据的查找效率。下面通过一个示例,了解一下到底什么是哈希表。
-
现在有 1/5/8/ 三个数字,你需要把这三个数字映射到数组中,由于哈希表规定必须使用下标来访问数据,因此需要构建一个 0 到 8 的数组,如下所示:
-
如上图所示,我们把待查找的数字,在相应的下标数组上标记出来,它们之间一一对应。虽然这样做能实现元素的查找,但却很浪费存储空间,并且查找效率也不高。而如果采用哈希表的话,我们只需要申请一个长度为 3 的数组(与待查找的元素个数相同),如下图所示:
-
将 1/5/8 分别对数组长度 3 做取模运算,然后把它们指向运算结果对应的数组槽位,这样就把一组离散的数据映射到了连续的空间中,从而在最大限度上提高了空间的利用率,并且也提高了元素的查找效率。但是会出现一个问题,数字 5、8 映射到同一个槽位上,这样就导致其中一个数字无法查找到。上述这种情况在实际中也会遇到,我们把它称为“哈希冲突”或者“哈希碰撞”。
-
有许多方法可以解决“哈希冲突”,比如开放地址法、链表地址法,再次散列法等,而 Redis 采用是链表地址法。这里只对链表地址法做简单介绍,这种方法就是将有冲突的数据使用链表把它们串联起来,这样即使发生了冲突,也可以将数据存储在一起,最后,通过遍历链表的方式就找到上述发生“冲突”的数据。如下所示:
如果值是字符串的话,就需要通过哈希函数将字符串转换成具体的数值,然后再对其进行映射。
11.2 常用命令汇总
命令 | 说明 |
---|---|
HDEL key field2 [field2] | 用于删除一个或多个哈希表字段。 |
HEXISTS key field | 用于确定哈希表字段是否存在。 |
HGET key field | 获取 key 关联的哈希字段的值。 |
HGETALL key | 获取 key 关联的所有哈希字段值。 |
HINCRBY key field increment | 给 key 关联的哈希字段做整数增量运算 。 |
HINCRBYFLOAT key field increment | 给 key 关联的哈希字段做浮点数增量运算 。 |
HKEYS key | 获取 key 关联的所有字段。 |
HLEN key | 获取 key 中的哈希表的字段数量。 |
HMSET key field1 value1 [field2 value2 ] | 在哈希表中同时设置多个 field-value(字段-值) |
HMGET key field1 [field2] | 用于同时获取多个给定哈希字段(field)对应的值。 |
HSET key field value | 用于设置指定 key 的哈希表字段和值(field/value)。 |
HSETNX key field value | 仅当字段 field 不存在时,设置哈希表字段的值。分布式锁场景:更新成功,version+1,否则version-1,取更新后的值 |
HVALS key | 用于获取哈希表中的所有值。 |
HSCAN key cursor | 迭代哈希表中的所有键值对,cursor 表示游标,默认为 0。 |
命令语法和string类型类似,只不过值是hash值
- 存储数据的思路:微博上好友关注时间的场景,这里以用户 关注时间 作为 key(user:attentiontime),field 字段表示好友的 ID,value 则代表好友关注用户(user:12)的时间。
local:0>hmset user:attentiontime user:2 20221202 user:3 20221101 user:4 20221005
"OK"
#迭代hash的key键
local:0>hscan user:attentiontime 0
1) "0"
2) 1) "user:2"
2) "20221202"
3) "user:3"
4) "20221101"
5) "user:4"
6) "20221005"
#设置单个字段
local:0>hset user:attentiontime user:5 20220909
"OK"
#查询单个字段
local:0>hget user:attentiontime user:2
"20221202"
#查询所有字段
local:0>hkeys user:attentiontime
1) "user:2"
2) "user:3"
3) "user:4"
4) "user:5"
#查询所有字段的值
local:0>hvals user:attentiontime
1) "20221202"
2) "20221101"
3) "20221005"
4) "20220909"
#查询所有字段和值
local:0>hgetall user:attentiontime
1) "user:2"
2) "20221202"
3) "user:3"
4) "20221101"
5) "user:4"
6) "20221005"
7) "user:5"
8) "20220909"
#返回字段个数
local:0>hlen user:attentiontime
"4"
#判断字段是否存在,存在返回1,不存在返回0
local:0>hexists user:attentiontime user:5
"1"
local:0>hexists user:attentiontime user:6
"0"
11.3 HSET、HGET命令
-
HSET 命令用于将哈希表 key 中的字段 field 的值设为 value。如果 key 不存在,将自动创建一个新的哈希表并进行 HSET 操作。如果 field 已经存,那么旧值将被覆盖。HSET 命令与 HMSET 命令相似,后者可以同时存储多个字段和字段值。命令可用版本:>= 2.0.0
-
Redis HGET 命令返回哈希表 key 中给定字段 field 的字段值。该命令与 HMGET 相似,后者可以返回哈希表 key 中,一个或多个给定的字段值。命令可用版本:>= 2.0.0。
-
命令的基本语法如下:
#HSET 命令的基本语法如下:
HSET key field value
# HGET命令的基本语法如下:
HGET key field
-
HSET命令的返回值:如果 field 是哈希表中的一个新字段,并且值设置成功,则返回 1;如果哈希表中 field 已经存在,并且旧值已被新值覆盖,则返回 0。
-
HGET 命令的返回值:给定 field 的值。当给定 filed 不存在或给定 key 不存在时,则返回 null。
-
命令演示
remote:0>hgetall user
1) "id:1"
2) "wu"
3) "id:2"
4) "li"
# 设置一个新字段
remote:0>hset user id:3 zhao
"1"
#覆盖字段值
remote:0>hset user id:3 wang
"0"
#当字段存在时
remote:0>hget user id:3
"wang"
#当字段(域)不存在时
remote:0>hget user id:4
null
remote:0>hgetall user
1) "id:1"
2) "wu"
3) "id:2"
4) "li"
5) "id:3"
6) "wang"
11.4 HDEL命令
- HDEL命令删除哈希表 key 中的一个或多个指定域,不存在的字段将被忽略。命令可用版本:>= 2.0.0。
注意:Redis2.4 以下的版本, HDEL 每次只能删除单个字段,如果一次性删除多个字段,请使用 Redis 事务命令 MULTI/EXEC。
- HDEL 命令的基本语法如下:
HDEL key field [field ...]
- HDEL 命令返回值:被成功移除的字段的数量,不包括被忽略的字段。命令演示:
remote:0>hmset user id:4 zhao id:5 han
"OK"
remote:0>hgetall user
1) "id:1"
2) "wu"
3) "id:2"
4) "li"
5) "id:3"
6) "wang"
7) "id:4"
8) "zhao"
9) "id:5"
10) "han"
#删除操作
remote:0>hdel user id:4 id:5
"2"
remote:0>hgetall user
1) "id:1"
2) "wu"
3) "id:2"
4) "li"
5) "id:3"
6) "wang"
11.5 HEXISTS命令
-
HEXISTS 命令用于查看哈希表 key 中,给定域 field 是否存在。命令可用版本:>= 2.0.0。
-
Redis HEXISTS 命令的基本语法如下:
HEXISTS key field
- HEXISTS 命令的返回值如果哈希表含有给字段,则返回 1;如果哈希表不含有给定字段,或者是 key 不存在,那么返回 0。命令演示
remote:0>hgetall user
1) "id:1"
2) "wu"
3) "id:2"
4) "li"
5) "id:3"
6) "wang"
#存在返回1
remote:0>hexists user id:3
"1"
#字段不存在返回0
remote:0>hexists user id:4
"0"
#key不存在返回0
remote:0>hexists users id:4
"0"
11.6 HGETALL命令
-
HGETALL 命令用于返回哈希表 key 中,所有的字段和值。命令可用版本:>= 2.0.0。
-
Redis HGETALL 命令的基本语法如下:
HGETALL key
- HGETALL 命令的返回值:以列表形式返回哈希表的字段和字段值。若 key 不存在,则返回空列表。命令演示
remote:0>hmset user id:4 zhao id:5 han
"OK"
#返回字段与字段值
remote:0>hgetall user
1) "id:1"
2) "wu"
3) "id:2"
4) "li"
5) "id:3"
6) "wang"
7) "id:4"
8) "zhao"
9) "id:5"
10) "han"
#key不存在
remote:0>hgetall users
11.7 HKEYS、HVALS命令
-
HKEYS 命令返回哈希表 key 中的所有字段。HVALS 命令返回哈希表 key 中所有字段的值。命令可用版本:>= 2.0.0。
-
命令的基本语法如下:
#HKEYS命令的基本语法如下:
HKEYS key
#HVALS命令的基本语法如下:
HVALS key
-
HKEYS 命令的返回值:一个包含哈希表中所有字段的列表。当 key 不存在时,返回一个空列表。
-
HVALS命令的返回值:一个包含哈希表中所有的字段值。当 key 不存在时,返回一个空列表。
-
命令演示
remote:0>hgetall user
1) "id:1"
2) "wu"
3) "id:2"
4) "li"
5) "id:3"
6) "wang"
7) "id:4"
8) "zhao"
9) "id:5"
10) "han"
#返回所有字段列表
remote:0>hkeys user
1) "id:1"
2) "id:2"
3) "id:3"
4) "id:4"
5) "id:5"
#key不存在时返回空列表
remote:0>hkeys users
#返回所有值列表
remote:0>hvals user
1) "wu"
2) "li"
3) "wang"
4) "zhao"
5) "han"
#key不存在时返回空列表
remote:0>hvals users
11.8 HINCRBY命令
-
HINCRBY 命令为哈希表 key 中的 field 的值加上增量 increment 。增量也可以为负数,相当于对给定字段的值进行减法操作。
-
如果 key 不存在,将自动创建一个新的哈希表并执行 HINCRBY 命令;如果域 field 不存在,那么在执行命令前,字段的值被初始化为 0。若对于一个储存字符串值的 field 执行 HINCRBY 命令将造成一个错误。该命令操作的数值被限制在 64 位(bit)有符号数字表示之内。命令可用版本:>= 2.0.0。
-
HINCRBY 命令的基本语法如下:
HINCRBY key field increment
- HINCRBY 命令的返回值:执行 HINCRBY 命令之后,哈希表 key 中 field 的值。命令演示
#自动创建新的字段和值
remote:0>hincrby counter price 100
"100"
#存在返回1
remote:0>hexists counter price
"1"
#不存在返回0
remote:0>hexists counter prices
"0"
#加减计算
remote:0>hincrby counter price 50
"150"
remote:0>hincrby counter price -50
"100"
11.9 HLEN命令
- HLEN 命令用于返回哈希表 key 中字段的数量。命令可用版本:>= 2.0.0。HLEN 命令的基本语法如下:
HLEN key
- HLEN 命令的返回值:哈希表中字段的数量。若 key 不存在时,则返回 0 。命令演示
remote:0>hgetall user
1) "id:1"
2) "wu"
3) "id:2"
4) "li"
5) "id:3"
6) "wang"
7) "id:4"
8) "zhao"
9) "id:5"
10) "han"
#返回所走字段列表
remote:0>hkeys user
1) "id:1"
2) "id:2"
3) "id:3"
4) "id:4"
5) "id:5"
remote:0>hlen user
"5"
#key不存在时
remote:0>hlen users
"0"
remote:0>hscan user 0
1) "0"
2) 1) "id:1"
2) "wu"
3) "id:2"
4) "li"
5) "id:3"
6) "wang"
7) "id:4"
8) "zhao"
9) "id:5"
10) "han"