Redis学习手册2—数据结构之散列

前言

在上一章《Redis学习手册1—数据结构之字符串》中,我们学习了Redis最基本的数据结构——字符串,但是我们发现,在使用字符串类型键存储具有关联关系的一系列属性时,我们不得不创建若干字符串键,分别存储不同的属性。例如,我们想要使用字符串描述一篇文章的属性:标题、内容、作者和创建时间。如果使用字符串类型键存储这些属性,我们至少需要使用四个字符串类型键来存储,然后定义相同的键的前缀,如:article::1::title、article::1::content、article::1::author、article::1::create_time。

虽然在技术上是可行的,但是要知道键本身也是数据,也是需要占用内存资源的,那么当数据量大的时候,就会造成不必要的内存浪费。为了解决这个问题,使得相关联的数据能够被打包起来存储,Redis提供了散列类型(hash) 键。

散列简介

Redis的 散列(hash) 键会将一个键和一个散列在数据库里关联起来,用户可以在散列中为任意多个 字段(field) 设置值。与字符串一样,散列的字段和值既可以是文本数据,也可以是二进制数据。

通过使用散列键,用户可以把相关联的多项数据存储到同一个散列里,以便对这些数据进行管理,或者针对它们执行批量操作。

散列存储结构

以文章数据为例,下图展示了散列键在内存中的存储结构:
在这里插入图片描述

散列键的功能

Redis为散列键提供了一系列操作命令,通过使用这些命令,用户可以:

  • 为散列的字段设置值,或者只在字段不存在的情况下为它设置值;
  • 从散列里面获取给定字段名的值;
  • 对存储着数字值的字段执行加法或减法操作;
  • 检查给定的字段是否存在于散列中;
  • 从散列中删除指定的字段;
  • 查看散列包含的字段数量;
  • 一次为散列的多个字段设置值,或者一次从散列中获取多个字段的值;
  • 获取散列包含的所有字段、所有值或所有字段和值;

散列键命令速查表

以下表格中,列举了散列键提供的所有命令及基本使用说明:

命令用法及参数说明
HSETHSET hash field value为散列中指定的字段设置值
HSETNXHSETNX hash field value只在指定字段不存在时设置值
HGETHGET hash field获取指定字段的值
HINCRBYHINCRBY hash field increment为给定的字段值执行加法运算,与字符串键的INCRBY命令用法一致
HINCRBYFLOATHINCRBYFLOAT hash field increment支持浮点数字段值的加法操作
HSTRLENHSTRLEN hash field获取给定字段值的字节长度
HEXISTSHEXISTS hash field判断给定的字段是否存在于散列中
HDELHDEL hash field删除指定字段及其关联的值
HMSETHMSET hash field value [field value ...]一次为多个字段设置值
HMGETHMGET hash field [field ...]一次获取多个字段的值
HKEYSHKEYS hash获取散列中包含的所有字段
HVALSHVALS hash获取散列中包含的所有值
HGETALLHGETALL hash获取散列中包含的所有字段和值

命令详解

HSET命令:为字段设置值

用户可以通过执行HSET命令为散列中指定的字段设置值:

HSET hash field value

根据给定的字段是否已经存在于散列中,HSET命令的行为也会有所不同:

  • 如果给定的字段不存在于散列中,那么这次设置就是一次创建操作,命令将在散列中关联起给定的字段和值,然后返回 1
  • 如果给定的字段已经存在于散列中,那么这次设置就是一次更新操作,命令将使用用户给定的新值去覆盖原来的值,然后返回 0
127.0.0.1:6379> HSET article::1 title "greeting"
(integer) 1
127.0.0.1:6379> HSET article::1 content "hello world"
(integer) 1
127.0.0.1:6379> HSET article::1 author "peter"
(integer) 1
127.0.0.1:6379> HSET article::1 create_time "2020-06-03 12:52:20"
(integer) 1
127.0.0.1:6379> HSET article::1 title "Redis Tutorial"
(integer) 0
127.0.0.1:6379> HSET article::1 content "Redis is a data structure store ..."
(integer) 0

复杂度: O ( 1 ) O(1) O(1)
版本要求:HSET命令从Redis 2.0.0版本开始可用。

HSETNX命令:只在字段不存在时为它设置值

HSETNX命令的作用和HSET命令的作用非常相似,区别在于,HSETNX命令只会在指定字段不存在时执行设置操作:

HSETNX hash field value

HSETNX命令在字段不存在并且成功设置值时返回 1,在字段已存在并导致设置操作失败时返回0

127.0.0.1:6379> HSETNX article::1 title "Redis Performance Test"
(integer) 0  -- 设置成功
127.0.0.1:6379> HSETNX article::1 view_count 100
(integer) 1

复杂度: O ( 1 ) O(1) O(1)
版本要求:HSETNX命令从Redis 2.0.0版本开始可用。

HGET命令:获取字段的值

HGET命令可以根据用户给定的字段,从散列中获取该字段的值:

HGET hash field
127.0.0.1:6379> HGET article::1 title
"greeting"
127.0.0.1:6379> HGET article::1 author
"peter"

当给定的字段或散列不存在时,那么HGET命令将返回一个空值:

127.0.0.1:6379> HGET account::54321 location
(nil)

复杂度: O ( 1 ) O(1) O(1)
版本要求:HGET命令从Redis 2.0.0版本可用。

HINCRBY命令

与字符串键的INCRBY命令一样,如果散列的字段里面存储着能够被Redis解释为整数的数字,那么用户就可以使用HINCRBY命令为该字段的值上加上指定的整数增量

HINCRBY hash field increment

HINCRBY命令在成功执行加法操作之后将返回字段当前的值作为命令的结果。

127.0.0.1:6379> HINCRBY article::1 view_count 1
(integer) 1

因为Redis只为散列提供了加法操作的HINCRBY命令,但是没有为散列提供相应的减法操作命令,因此可以使用传入负数增量给HINCRBY命令,即可执行减法操作:

127.0.0.1:6379> HSET hash article::1 view_counte 100
(integer) 1
127.0.0.1:6379> HINCRBY hash article::1 view_counte 50
(integer) 50

HINCRBY命令只能作用于字段值为整数,且增量也只能为整数,否则Redis会返回错误。

复杂度: O ( 1 ) O(1) O(1)
版本要求:HNICRBY命令从Redis 2.0.0版本开始可用。

HINCRBYFLOAT命令

HINCRBYFLOAT命令和HINCRBY命令的作用类似,它们之间的主要区别在于HINCRBYFLOAT命令不仅可以使用整数作为增量,还可以使用浮点数作为增量:

HINCRBYFLOAT hash field increment

HINCRBYFLOAT命令在执行成功后,将返回给定字段的当前值作为结果。

127.0.0.1:6379> HGET fruit::apple price
"5.60"
127.0.0.1:6379> HINCRBYFLOAT fruit::apple price 1.4
"7"

如果加法计算的结果能够被表示为整数,那么HINCRBYFLOAT命令将使用整数作为计算结果。

同样的,Redis也没有提供与HINCRBYFLOAT命令对应的减法操作命令,因此,可以通过传入负数增量的方式来执行减法操作。

复杂度: O ( 1 ) O(1) O(1)
版本要求:HINCRBYFLOAT命令从Redis 2.0.0版本开始可用。

HSTRLEN命令

用户可以使用HSTRLEN命令获取给定字段值的字节长度:

HSTRLEN hash field
127.0.0.1:6379> HSET article::10086 title "hello world"
(integer) 1
127.0.0.1:6379> HSTRLEN article::10086 title
(integer) 11

如果给定的字段或散列不存在,那么HSTRLEN命令将返回0

复杂度: O ( 1 ) O(1) O(1)
版本要求:HSTRLEN命令从Redis 3.2.0版本开始可用。

HEXISTS命令

HEXISTS命令可用于检查用户给定的字段是否存在于散列中:

HEXISTS hash field

如果散列中包含给定的字段,那么命令返回1;如果给定的字段或散列不存在,则返回 0

127.0.0.1:6379> HEXISTS hash not-exists-field
(integer) 0
127.0.0.1:6379> HEXISTS not-exists-hash not-exists-field
(integer) 0
127.0.0.1:6379> HEXISTS exists-hash exists-field
(integer) 1

复杂度: O ( 1 ) O(1) O(1)
版本要求:HEXISTS命令从Redis 2.0.0版本开始可用。

HDEL命令

HDEL命令用于删除散列中的指定字段及其相关联的值:

HDEL hash field

HDEL命令删除执行成功后返回1;如果给定的字段或给定的散列不存在,那么返回 0

127.0.0.1:6379> HDEL article::1 author
(integer) 1
127.0.0.1:6379> HDEL not-exists-hash not-exists-field
(integer) 0

复杂度: O ( 1 ) O(1) O(1)
版本要求:HDEL命令从Redis 2.0.0版本开始可用。

HLEN命令

HLEN命令可以获取散列中包含的字段数量:

HLEN hash
127.0.0.1:6379> HLEN article::1
(integer) 4

如果给定的散列不存在,那么返回0

复杂度: O ( 1 ) O(1) O(1)
版本要求:HLEN命令从Redis 2.0.0版本开始可用。

HMSET命令

用户可以使用HMSET命令一次为散列中的多个字段设置值:

HMSET hash field value [field value]

HMSET命令在设置成功时返回OK。

127.0.0.1:6379> HMSET article::10086 title "greeting" content "hello world" author "peter" 
OK

如果使用HSET命令执行上述操作,那么需要两次才能完成,而使用HMSET命令却只需要一次就可以,因此可以减少与Redis服务器的通信次数,提高执行速度。
HSET命令一样,如果指定的字段已存在于散列中,那么就使用新值去覆盖旧值。

复杂度: O ( N ) O(N) O(N),其中 N N N为被设置的字段数量。
版本要求:HMSET命令从Redis 2.0.0版本开始可用。

HMGET命令

HMSET命令相对应的,HMGET命令可以一次返回多个字段的值:

HMGET hash field [field ...]

HMGET命令将按照用户给定的字段的顺序依次返回与之对应的值。

127.0.0.1:6379> HMGET article::10086 title content
1) "greeting"
2) "hello world"

HGET命令一样,如果给定的字段或散列不存在,那么将返回空值 **(nil)**作为结果。

复杂度: O ( N ) O(N) O(N),其中 N N N为用户给定的字段数量。
版本要求:HMGET命令从Redis 2.0.0版本开始可用。

HKEYS、HVALS、HGETALL命令

Redis为散列提供了HKEYSHVALSHGETALL这3个命令,可以分别用于获取散列包含的所有字段、所有值以及所有字段和值:

HKEYS hash
HVALS hash
HGETALL hash
127.0.0.1:6379> HKEYS article::10086
1) "title"
2) "content"
127.0.0.1:6379> HVALS article::10086
1) "greeting"
2) "hello world"
127.0.0.1:6379> HGETALL article::10086
1) "title"   -- 字段
2) "greeting"   -- 字段的值
3) "content"
4) "hello world"

如果给定的散列并不存在,那么HKEYSHVALSHGETALL都将返回一个空列表。

Redis散列包含的字段在底层是以无序的方式存储的,根据字段插入的顺序不同,包含相同字段的散列在执行HKEYSHVALSHGETALL命令时可能会得到不同的结果,因此在使用这3个命令时,不应该对它们的结果顺序做任何假设。

复杂度:HKEYS命令、HVALS命令和HGETALL命令的复杂度都为 O ( N ) O(N) O(N),其中 N N N为散列包含的字段数量。

版本要求:HKEYS命令、HVALS命令和HGETALL命令都从Redis 2.0.0版本开始可用。

散列与字符串

类似的命令

以下表格展示了字符串命令与类似的散列命令:

字符串散列
SET——为一个字符串键设置值HSET——为散列的给定字段设置
SETNX——仅在字符串键不存在的时为它设置值HSETNX——仅在散列不包含指定字段时设置值
GET——获取字符串的值HGET——获取散列指定字段的值
STRLEN——获取字符串值的字节长度HSTRLEN——获取给定字段值的字节长度
INCRBY——对字符串键的值执行整数加法操作HINCRBY——对字段存储的数字值执行整数加法操作
INCRBYFLOAT——对字符串键存储的浮点数执行浮点数加法操作HINCRBYFLOAT——对字段存储的数字值执行浮点数加法操作
MSET——一次为多个字符串键设置值HMSET——一次为多个字段设置值
MGET——一次获取多个字符串键的值HMGET——一次获取多个字段的值
EXISTS——检查给定的键是否存在于数据库中,可用于所有类型的键HEXISTS——检查给定的字段是否存在于散列中
DEL——从数据库中删除指定的键,适用于所有类型的键HDEL——从散列中删除指定的字段及它的值

散列键的优点

散列键的最大优点,就是它只要在数据库中创建一个键,就可以把任意多个字段和值存储到散列里。相反,字符串键只能存储一个键值对。

字符串键的优点

虽然使用散列键可以有效的节约资源并更好的组织数据,但是字符串键也有自己的优点:

  • 虽然散列键命令和字符串键命令在部分功能上有重合的地方,但是字符串键命令提供的操作比散列键命令更丰富。比如,字符串能够使用SETRANGE命令和GETRANGE命令设置或读取字符串值的部分内容,或者使用APPEND命令进行内容的追加,而散列键并不支持这些操作。
  • 键的过期,因为Redis的键过期功能是针对整个键的,而散列键中包含的多个字段和值,却不能为各个字段设置不同的过期时间。

字符串键和散列键的选择

从资源占用、支持的操作及过期时间3个方面对比字符串和散列键的优缺点:
在这里插入图片描述
结合上述对比结果,总结了一些选择的条件和方法:

  • 如果程序需要为每个数据项单独设置过期时间,那么使用字符串键;
  • 如果程序需要对数据项执行诸如SETRANGEGETRANGE或者APPEND等操作,那么优先考虑使用字符串键;
  • 如果程序需要存储的数据项比较多,并且你希望尽可能的减少内存占用,优先使用散列键;
  • 如果多个数据项在逻辑上属于同一组或者同一类,那么优先使用散列键;

上一篇:Redis学习手册1—数据结构之字符串

下一篇:Redis学习手册3—数据结构之列表

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值