hash

概述

在Redis中,哈希类型是指值本身有是一个键值对结构,形如:

1
2
key = "key",
value = {{field1, value1}, {field2, value2}}

alt text{: height=75%, width=75%}

注意:哈希类型中的映射关系通常为field-value,用于区分Redis整体的键值对(key-value),注意的是这里的value是指field对应的值,不是键(key)对应的值。

相关命令

HSET

功能:设置hash中指定的字段(field)和值(value)
时间复杂度:$O(1)$
返回值:添加的字段的个数

1
2
3
4
5
6
HSET key field value [field value..]

127.0.0.1:6379> hset key f1 111
(integer) 1
127.0.0.1:6379> hset key f2 222 f3 333 f4 444
(integer) 3

HGET

功能:获取hash中指定的字段的值(value)
时间复杂度:$O(1)$
返回值:值

1
2
3
4
5
6
HSET key field

127.0.0.1:6379> hget key f1
"111"
127.0.0.1:6379> hget key f2
"222"

HEXISTS

功能:判断hash中是否有指定字段
时间复杂度:$O(1)$
返回:是否存在

1
2
3
4
5
6
7
8
HEXISTS key field

127.0.0.1:6379> hexists key f1
(integer) 1
127.0.0.1:6379> hexists key f2
(integer) 1
127.0.0.1:6379> hexists key f10
(integer) 0

HDEL

功能:删除hash中的指定字段
时间复杂度:$O(1)$
返回值:本次操作删除字段的个数

1
2
3
4
HDEL key field

127.0.0.1:6379> hdel key f4
(integer) 1

DEL

功能:删除key,删除key所对应的哈希表

1
2
3
4
DEL key 

127.0.0.1:6379> del key
(integer) 1

HKEYS

功能:获取hash中所有的字段,会根据key找到对应的hash,然后再遍历hash
时间复杂度:$O(N)$,N表示key对应hash表中键值对的数目

1
2
3
4
5
6
7
8
9
HKEYS key

127.0.0.1:6379> hset key f1 111 f2 222 f3 333 f4 444
(integer) 4
127.0.0.1:6379> hkeys key
1) "f1"
2) "f2"
3) "f3"
4) "f4"

HVALUES

功能:能够获取到hash中所有的value

1
2
3
4
5
6
7
HVALS key

127.0.0.1:6379> hvals key
1) "111"
2) "222"
3) "333"
4) "444"

HGETALL

功能:获取hash中的所有字段以及对应的值。

1
2
3
4
5
6
7
8
9
10
11
HGETALL key

127.0.0.1:6379> hgetall key
1) "f1"
2) "111"
3) "f2"
4) "222"
5) "f3"
6) "333"
7) "f4"
8) "444"

HMGET

功能:一次查询多个field,可以减少网络通讯带来的开销

1
2
3
4
127.0.0.1:6379> hmget key f1 f2 f3
1) "342"
2) "222"
3) "333"

HMSET

功能:一次设置多个field-value键值对,可以减少网络通讯带来的开销。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
127.0.0.1:6379> hmset key f1 f2 f3

127.0.0.1:6379> hmset key f5 555 f6 666
OK
127.0.0.1:6379> hgetall key
1) "f1"
2) "342"
3) "f2"
4) "222"
5) "f3"
6) "333"
7) "f4"
8) "444"
9) "f5"
10) "555"
11) "f6"
12) "666"

HSCAN

功能:遍历redishash,但是它属于渐进式遍历。敲一次命令,遍历一小部分,再敲一次,再遍历一小部分。执行多次就可以完成整个的遍历过程。

1
HSCAN key cursor [MATCH pattern] [COUNT count]

参数:

  • key:哈希表的键。
  • cursor:迭代器的游标,初始值为0,表示迭代开始。每次HSCAN命令执行后,都会返回一个新的游标值,用于下一次迭代。
  • MATCH pattern(可选):指定一个模式,只有符合模式的字段才会被返回。
  • COUNT count(可选):指定每次迭代返回的最大元素数量。

返回值:HSCAN命令返回两个值:

  • 字符串形式的游标:用于下一次迭代的游标值。如果返回0,表示迭代完成。
  • 数组:包含两个元素,第一个元素是一个空数组(表示没有键被删除),第二个元素是一个包含键值对的数组。这些键值对是在当前迭代中返回的元素。
1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> hscan key 0
1) "0"
2) 1) "f1"
1) "111"
2) "f2"
3) "222"
4) "f3"
5) "333"
6) "f4"
7) "444"

HLEN

时间复杂度:$O(1)$
功能:获取Hash的个数

1
2
3
4
HLEN key

127.0.0.1:6379> hlen key
(integer) 4

HSETNX

功能:设置哈希表键值对,对于不存在的键值对,才能设置成功
时间复杂度:$O(1)$

1
2
3
4
5
6
HSETNX key filed value

127.0.0.1:6379> hsetnx key f5 555
(integer) 1
127.0.0.1:6379> hsetnx key f1 111
(integer) 0

HINCRBY

功能:将hash中字段对应的数值添加指定的值。hash这里的value,也可以当做数组处理。hincrby就可以加减整数,hincrbyfloat就可以加减小数。

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> hincrby key f1 231
(integer) 342
127.0.0.1:6379> hgetall key
1) "f1"
2) "342"
3) "f2"
4) "222"
5) "f3"
6) "333"
7) "f4"
8) "444"

命令总结

命令 执行效果 时间复杂度
hset key field value 设置值 O(1)
hget key field 获取值 O(1)
hset key field [field....] 删除field O(k)
hlen key 计算field个数 O(1)
hgetall key 获取所有的field-value O(k)
hmget field [field...] 批量获取field-value O(k)
hmget key field value [field value...] 批量获取filed-value O(k)
hexists key field 判断field是否存在 O(1)
hkeys key 获取所有的field O(k)
hvals key 获取所有的value O(k)
hkeys key 获取所有的field O(k)
hsetnx key field value 设置值,但必须在field不存在时才能设置成功 O(1)
hincrby key field n 对于filed-value + n O(1)
hincrbyfloat key field n 对于filed-value + n O(1)
hstrlen key field n 计算value的字符串长度 O(1)

内部编码

哈希的内部编码有两种

  • ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplst-entries配置(默认512个),同时所有值都小于hash-max-ziplist-value配置(默认64字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。
  • hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使用hashtable作为哈希的内部实现,因此此时ziplist的读写效率会下降,而hashtable的读写时间复杂度为$O(1)$
  1. 如果是哈希中的元素个数较少,使用ziplist表示,元素个数较多,使用hashtable表示
  2. 每个value的值长度都比较短,使用ziplist表示,如果某个value的长度太长了,也会转换成hashtable
1
2
3
4
5
6
7
8
127.0.0.1:6379> hset key f1 111
(integer) 1
127.0.0.1:6379> object encoding key
"ziplist"
127.0.0.1:6379> hset key f2 222222.....22222222222222222222222222222222222222222
(integer) 1
127.0.0.1:6379> object encoding key
"hashtable"

使用场景

缓存

下图为关系型数据表记录的两条用户信息,用户的属性表现为表的列,每条用户信息表现为行。

alt text{: height=75%, width=75%}

下图为映射关系表示这两个用户信息。

alt text{: height=75%, width=75%}

上述场景string类型也可以做到,就需要用到json这样的数据格式。但是我们只是想要里面的某一个field,或者修改某个field。就需要把整个json都读出来,解析成对象,操作field,再重写转成json字符串,再写回去。

如果使用hash方式表示Userinfo,就可以使用field表示对象的某个属性,此时就可以非常方便的修改/获取任何一个属性的值了。此方式确实读写field更直观高效,但是要付出空间代价。需要控制哈希在ziplisthashtable两种内部编码的转换,可能会造成内存的较大消耗。

原生字符串类型

1
2
3
set user:1:name James
set user:1:age 23
set user:1:city Beijing
  • 优点:实现简单
  • 缺点:占用过多的键,内存占用量过大,同时用户信息在Redis中比较分散,减少内聚性,所以这种方案基本没有实用性。

序列化字符串类型

1
set user:1 经过序列化后的用户对象字符串
  • 优点:针对总是以整体作为操作的信息比较合适,编程也简单。同时,如果序列化方案选择合适,内存的使用效率很高。
  • 缺点:本身序列化和反序列化需要一定开销,同时如果总是操作个别属性则非常不灵活

哈希类型

1
hmset user:1 name James age 23 city Beijing
  • 优点:简单、灵活、直观。尤其是针对信息的局部变更或者获取操作。
  • 缺点:需要控制哈希在ziplist和hashtable两种内部编码的转换,可能会造成内存的较大消耗