当前位置: 首页 > article >正文

Redis - Hash 哈希

一、基本认识

        ⼏乎所有的主流编程语⾔都提供了哈希(hash)类型,它们的叫法可能是哈希、字典、关联数 组、映射。在Redis中,哈希类型是指值本⾝⼜是⼀个键值对结构,形如key="key",value={{ field1, value1 }, ..., {fieldN, valueN } },Redis 键值对和哈希类型⼆者的关系可以⽤图2-15来表⽰。

图2-15字符串和哈希类型对⽐

         哈希类型中的映射关系通常称为field-value,⽤于区分Redis整体的键值对(key-value), 注意这⾥的value是指field对应的值,不是键(key)对应的值,请注意value在不同上下 ⽂的作⽤。

 

二、命令

2.1、HSET

设置hash中指定的字段(field)的值(value)

语法:

HSET key field value [field value ...]

命令有效版本:2.0.0之后

时间复杂度:插⼊⼀组field为O(1),插⼊N组field为O(N)

返回值:添加的字段的个数。

⽰例:

 redis> HSET myhash field1 "Hello"
 (integer) 1
 redis> HGET myhash field1
 "Hello"

2.2、HGET

获取hash中指定字段的值。

语法:

 HGET key field

命令有效版本:2.0.0之后

时间复杂度:O(1)

返回值:字段对应的值或者nil。

⽰例:

 redis> HSET myhash field1 "foo"
 (integer) 1
 redis> HGET myhash field1
 "foo"
 redis> HGET myhash field2
 (nil)

2.3、HEXISTS

判断hash中是否有指定的字段。

语法:

 HEXISTS key field

命令有效版本:2.0.0之后

时间复杂度:O(1)

返回值:1表⽰存在,0表⽰不存在。

⽰例:

 redis> HSET myhash field1 "foo"
 (integer) 1
 redis> HEXISTS myhash field1
 (integer) 1
 redis> HEXISTS myhash field2
 (integer) 0

2.4、HDEL

删除hash中指定的字段。

语法:

HDEL key field [field ...]

命令有效版本:2.0.0之后

时间复杂度:删除⼀个元素为O(1).删除N个元素为O(N)

返回值:本次操作删除的字段个数

⽰例:

 redis> HSET myhash field1 "foo"
 (integer) 1
 redis> HDEL myhash field1
 (integer) 1
 redis> HDEL myhash field2
 (integer) 0

2.5、HKEYS

获取hash中的所有字段。

语法:

HKEYS key

命令有效版本:2.0.0之后

时间复杂度:O(N) , N为field的个数.

返回值:字段列表。

⽰例:

 redis> HSET myhash field1 "Hello"
 (integer) 1
 redis> HSET myhash field2 "World"
 (integer) 1
 redis> HKEYS myhash
 1) "field1"
 2) "field2"

2.6、HVALS

获取hash中的所有的值。

语法:

HVALS key

命令有效版本:2.0.0之后

时间复杂度:O(N),N为field的个数.

返回值:所有的值。

⽰例:

 redis> HSET myhash field1 "Hello"
 (integer) 1
 redis> HSET myhash field2 "World"
 (integer) 1
 redis> HVALS myhash
 1) "Hello"
 2) "World"

2.7、HGETALL

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

语法:

HGETALL key

命令有效版本:2.0.0之后

时间复杂度:O(N),N为field的个数.

返回值:字段和对应的值。

⽰例:

 redis> HSET myhash field1 "Hello"
 (integer) 1
 redis> HSET myhash field2 "World"
 (integer) 1
 redis> HGETALL myhash
 1) "field1"
 2) "Hello"
 3) "field2"
 4) "World"

2.8、HMGET

⼀次获取hash中多个字段的值。

语法:

 HMGET key field [field ...]

命令有效版本:2.0.0之后

时间复杂度:只查询⼀个元素为O(1),查询多个元素为O(N),N为查询元素个数.

返回值:字段对应的值或者nil。

⽰例:

redis> HSET myhash field1 "Hello"
(integer) 1
 redis> HSET myhash field2 "World"
 (integer) 1
 redis> HMGET myhash field1 field2 nofield
 1) "Hello"
 2) "World"
 3) (nil)

        在使⽤HGETALL时,如果哈希元素个数⽐较多,会存在阻塞Redis的可能。如果开发⼈员只 需要获取部分field,可以使⽤HMGET,如果⼀定要获取全部field,可以尝试使⽤HSCAN 命令,该命令采⽤渐进式遍历哈希类型。

2.9、HLEN

获取hash中的所有字段的个数。

语法:

HLEN key

命令有效版本:2.0.0之后

时间复杂度:O(1)

返回值:字段个数。

⽰例:

 redis> HSET myhash field1 "Hello"
 (integer) 1
 redis> HSET myhash field2 "World"
 (integer) 1
 redis> HLEN myhash
 (integer) 2

2.10、HSETNX

在字段不存在的情况下,设置hash中的字段和值。

语法:

HSETNX key field value

命令有效版本:2.0.0之后

时间复杂度:O(1)

返回值:1表⽰设置成功,0表⽰失败。

⽰例:

 redis> HSETNX myhash field "Hello"
 (integer) 1
 redis> HSETNX myhash field "World"
 (integer) 0
 redis> HGET myhash field
 "Hello"

2.11、HINCRBY

将 hash 中字段对应的数值添加指定的值。

语法:

 HINCRBY key field increment

命令有效版本:2.0.0之后

时间复杂度:O(1)

返回值:该字段变化之后的值。

⽰例:

 redis> HSET myhash field 5
 (integer) 1
 redis> HINCRBY myhash field 1
 (integer) 6
 redis> HINCRBY myhash field -1
 (integer) 5
 redis> HINCRBY myhash field -10
 (integer) -5

2.12、HINCRBYFLOAT

HINCRBY的浮点数版本。

语法:

 HINCRBYFLOAT key field increment

命令有效版本:2.6.0之后

时间复杂度:O(1)

返回值:该字段变化之后的值。

⽰例:

 redis> HSET mykey field 10.50
 (integer) 1
 redis> HINCRBYFLOAT mykey field 0.1
 "10.6"
 redis> HINCRBYFLOAT mykey field -5
 "5.6"
 redis> HSET mykey field 5.0e3
 (integer) 0
 redis> HINCRBYFLOAT mykey field 2.0e2
 "5200"

2.13、命令⼩结

        表 2-4 是哈希类型命令的效果、时间复杂度,开发⼈员可以参考此表,结合⾃⾝业务需求和数据 ⼤⼩选择合适的命令。

表 2-4 哈希类型命令⼩结

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

三、内部编码

哈希的内部编码有两种:

  • ziplist(压缩列表):当哈希类型元素个数⼩于hash-max-ziplist-entries配置(默认512个)、 同时所有值都⼩于hash-max-ziplist-value配置(默认64字节)时,Redis会使⽤ziplist作为哈 希的内部实现,ziplist使⽤更加紧凑的结构实现多个元素的连续存储,所以在节省内存⽅⾯⽐ hashtable更加优秀。
  • hashtable(哈希表):当哈希类型⽆法满⾜ziplist的条件时,Redis会使⽤hashtable作为哈希 的内部实现,因为此时ziplist的读写效率会下降,⽽hashtable的读写时间复杂度为O(1)。

下⾯的⽰例演⽰了哈希类型的内部编码,以及响应的变化。

1)当field个数⽐较少且没有⼤的value时,内部编码为ziplist:

 127.0.0.1:6379> hmset hashkey f1 v1 f2 v2
 OK
 127.0.0.1:6379> object encoding hashkey
 "ziplist"

2)当有value⼤于64字节时,内部编码会转换为hashtable:

 127.0.0.1:6379> hset hashkey f3 "one string is bigger than 64 bytes ... 省略..."
 OK
 127.0.0.1:6379> object encoding hashkey
 "hashtable"

3)当field个数超过512时,内部编码也会转换为hashtable:

 127.0.0.1:6379> hmset hashkey f1 v1 h2 v2 f3 v3 ... 省略 ... f513 v513
 OK
 127.0.0.1:6379> object encoding hashkey
 "hashtable"

四、使用场景

        图2-16为关系型数据表记录的两条⽤⼾信息,⽤⼾的属性表现为表的列,每条⽤⼾信息表现为⾏。如果映射关系表⽰这两个⽤⼾信息,则如图2-17所⽰。

图2-16关系型数据表保存⽤⼾信息

uidnameagecity
1James28Beijing
2Johnathan30Xian

图2-17映射关系表⽰⽤⼾信息

 相⽐于使⽤JSON格式的字符串缓存⽤⼾信息,哈希类型变得更加直观,并且在更新操作上变得 更灵活。可以将每个⽤⼾的id定义为键后缀,多对field-value对应⽤⼾的各个属性。

但是需要注意的是哈希类型和关系型数据库有两点不同之处:

  • 哈希类型是稀疏的,⽽关系型数据库是完全结构化的,例如哈希类型每个键可以有不同的field,⽽ 关系型数据库⼀旦添加新的列,所有⾏都要为其设置值,即使为null,如图2-18所⽰。
  • 关系数据库可以做复杂的关系查询,⽽Redis去模拟关系型复杂查询,例如联表查询、聚合查询等 基本不可能,维护成本⾼。

图2-18关系型数据库稀疏性

五、缓存⽅式对⽐

        截⾄⽬前为⽌,已经能够⽤三种⽅法缓存⽤⼾信息,下⾯给出三种⽅案的实现⽅法和优缺点 分析。

1. 原⽣字符串类型⸺使⽤字符串类型,每个属性⼀个键。

 set user:1:name James
 set user:1:age 23
 set user:1:city Beijing

优点:实现简单,针对个别属性变更也很灵活。

缺点:占⽤过多的键,内存占⽤量较⼤,同时⽤⼾信息在Redis中⽐较分散,缺少内聚性,所以这种 ⽅案基本没有实⽤性。

2. 序列化字符串类型,例如JSON格式

set user:1 经过序列化后的⽤⼾对象字符串

优点:针对总是以整体作为操作的信息⽐较合适,编程也简单。同时,如果序列化⽅案选择合适,内 存的使⽤效率很⾼。

缺点:本⾝序列化和反序列需要⼀定开销,同时如果总是操作个别属性则⾮常不灵活。

3. 哈希类型

hmset user:1 name James age 23 city Beijing

优点:简单、直观、灵活。尤其是针对信息的局部变更或者获取操作。

缺点:需要控制哈希在ziplist和hashtable两种内部编码的转换,可能会造成内存的较⼤消耗。


http://www.kler.cn/a/390492.html

相关文章:

  • 贪心算法入门(二)
  • VMware虚拟机安装Win7专业版保姆级教程(附镜像包)
  • unity基础,点乘叉乘。
  • AtomicInteger 和 AtomicIntegerFieldUpdater的区别
  • 实验一:自建Docker注册中心
  • ODOO学习笔记(1):ODOO的SWOT分析和技术优势是什么?
  • MR30分布式IO热插拔:智能时代的便捷与高效
  • uni-app小程序echarts中tooltip被遮盖
  • ★ 算法OJ题 ★ 前缀和算法(下)
  • [OS] 区分按位与()和逻辑与()
  • C# 如何将winform只生成一个绿色文件?
  • 02-1_MVCC版本链清理
  • 手写一些方法
  • Mac保护电池健康,延长电池使用寿命的好方法
  • 十六:Spring Boot依赖 (1)-- spring-boot-starter 依赖详解
  • 捕获抖音截图:如何用Puppeteer保存页面状态
  • linux 通过apt安装软件包时出现依赖包版本不对的问题解决
  • 我谈维纳(Wiener)复原滤波器
  • ChatGPT 通过三种方式帮助我进行学术写作
  • 编程之路,从0开始:练习篇
  • Maven最佳实践
  • 嵌入式ARM平台Linux网络实时性能优化
  • Spring Plugin与策略模式:打造动态可扩展的应用
  • 大数据技术在智慧医疗中的应用
  • 期刊论文查重率多少,才会不被认定为学术不端?
  • CSS的定位(文档流,相对定位,绝对定位,固定定位)