Redis:Hash数据类型

🌈 个人主页:Zfox_

🔥 系列专栏: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在不同上下⽂的作⽤。

🔥 命令

🦋 HSET

🦈 设置hash中指定的字段(field)的值(value)(只能是字符串)。

语法:

cpp 复制代码
HSET key field value [field value ...]

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

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

示例:

cpp 复制代码
redis> HSET myhash field1 "Hello"
(integer) 1
redis> HGET myhash field1
"Hello"

🦋 HGET

获取hash中指定字段的值。

语法:

cpp 复制代码
HGET key field

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

⽰例:

cpp 复制代码
redis> HSET myhash field1 "foo"
(integer) 1
redis> HGET myhash field1
"foo"
redis> HGET myhash field2
(nil)

🦋 HEXISTS

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

语法:

cpp 复制代码
HEXISTS key field

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

⽰例:

cpp 复制代码
redis> HSET myhash field1 "foo"
(integer) 1
redis> HEXISTS myhash field1
(integer) 1
redis> HEXISTS myhash field2
(integer) 0

🦋 HDEL

删除hash中指定的字段。

语法:

cpp 复制代码
HDEL key field [field ...]

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

⽰例:

cpp 复制代码
redis> HSET myhash field1 "foo"
(integer) 1
redis> HDEL myhash field1
(integer) 1
redis> HDEL myhash field2
(integer) 0

🦋 HKEYS

获取hash中的所有字段。

语法:

cpp 复制代码
HKEYS key

返回值:字段列表。

⽰例:

cpp 复制代码
redis> HSET myhash field1 "Hello"
(integer) 1
redis> HSET myhash field2 "World"
(integer) 1
redis> HKEYS myhash
1) "field1"
2) "field2"

🦋 HVALS

获取hash中的所有的值。

语法 :

cpp 复制代码
HVALS key

返回值:所有的值。

⽰例:

cpp 复制代码
redis> HSET myhash field1 "Hello"
(integer) 1
redis> HSET myhash field2 "World"
(integer) 1
redis> HVALS myhash
1) "Hello"
2) "World"

🦋 HGETALL

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

语法:

cpp 复制代码
HGETALL key

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

⽰例:

cpp 复制代码
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"

🦋 HMGET

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

语法:

cpp 复制代码
HMGET key field [field ...]

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

⽰例:

cpp 复制代码
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)

🦋 HLEN

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

语法:

cpp 复制代码
HLEN key

返回值:字段个数。

⽰例:

cpp 复制代码
redis> HSET myhash field1 "Hello"
(integer) 1
redis> HSET myhash field2 "World"
(integer) 1
redis> HLEN myhash
(integer) 2

🦋 HSETNX

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

语法:

cpp 复制代码
HSETNX key field value

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

⽰例:

cpp 复制代码
redis> HSETNX myhash field "Hello"
(integer) 1
redis> HSETNX myhash field "World"
(integer) 0
redis> HGET myhash field
"Hello"

🦋 HINCRBY

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

语法:

cpp 复制代码
HINCRBY key field increment

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

⽰例:

cpp 复制代码
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

🦋 HINCRBYFLOAT

HINCRBY的浮点数版本

语法:

cpp 复制代码
HINCRBYFLOAT key field increment

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

⽰例:

cpp 复制代码
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-4是哈希类型命令的效果、时间复杂度,开发⼈员可以参考此表,结合⾃⾝业务需求和数据⼤⼩选择合适的命令。

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

🔥 内部编码

哈希的内部编码有两种:

  • 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:
cpp 复制代码
127.0.0.1:6379> hmset hashkey f1 v1 f2 v2
OK
127.0.0.1:6379> object encoding hashkey
"ziplist"
  1. 当有value⼤于64字节时,内部编码会转换为hashtable:
cpp 复制代码
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"
  1. 当field个数超过512时,内部编码也会转换为hashtable:
cpp 复制代码
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关系型数据表保存⽤户信息
图2-17映射关系表⽰⽤户信息

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

cpp 复制代码
UserInfo getUserInfo(long uid) {
	// 根据 uid 得到 Redis 的键
	String key = "user:" + uid;
	// 尝试从 Redis 中获取对应的值
	userInfoMap = Redis 执⾏命令:hgetall key;
	// 如果缓存命中(hit)
	if (value != null) {
		// 将映射关系还原为对象形式
		UserInfo userInfo = 利⽤映射关系构建对象(userInfoMap);
		return userInfo;
	}
	// 如果缓存未命中(miss)
	// 从数据库中,根据 uid 获取⽤⼾信息
	UserInfo userInfo = MySQL 执⾏ SQL:select * from user_info where uid =
	<uid>
	// 如果表中没有 uid 对应的⽤户信息
	if (userInfo == null) {
		响应 404
		return null;
	}
	// 将缓存以哈希类型保存
	Redis 执⾏命令:hmset key name userInfo.name age userInfo.age city
	userInfo.city
	// 写⼊缓存,为了防⽌数据腐烂(rot),设置过期时间为 1 ⼩时(3600 秒)
	Redis 执⾏命令:expire key 3600
	// 返回⽤⼾信息
	return userInfo;
}

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

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

🔥 缓存⽅式对⽐

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

分析。

  1. 原⽣字符串类型⸺使⽤字符串类型,每个属性⼀个键。
cpp 复制代码
set user:1:name James
set user:1:age 23
set user:1:city Beijing

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

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

  1. 序列化字符串类型,例如JSON格式
cpp 复制代码
set user:1    经过序列化后的⽤⼾对象字符串

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

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

  1. 哈希类型
cpp 复制代码
hmset user:1 name James age 23 city Beijing

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

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

🔥 共勉

😋 以上就是我对 Redis:Hash数据类型 的理解, 觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~ 😉

相关推荐
韩立学长1 小时前
基于Springboot的旧时月历史论坛4099k6s9(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
cccccc语言我来了1 小时前
(Linux (6):从包管理到工具探索,构建系统操作基础认知)
linux·运维·服务器
TDengine (老段)2 小时前
TDengine 字符串函数 CONCAT_WS 用户手册
android·大数据·数据库·时序数据库·tdengine·涛思数据
wuk9982 小时前
CentOS7环境搭建L2TP服务器
运维·服务器
恒创科技HK2 小时前
香港1核2G云服务器当网站服务器够用不?
运维·服务器
IT 小阿姨(数据库)2 小时前
PostgreSQL 之上的开源时序数据库 TimescaleDB 详解
运维·数据库·sql·postgresql·开源·centos·时序数据库
JanelSirry3 小时前
缓存击穿,缓存穿透,缓存雪崩的原因和解决方案(或者说使用缓存的过程中有没有遇到什么问题,怎么解决的)
缓存
学习3人组3 小时前
Node.js 网站服务器开发
运维·服务器·node.js
来知晓3 小时前
Linux:WSL内存空间管理之清完内存C盘可用空间不增问题解决
linux·运维·服务器
0南城逆流03 小时前
【STM32】知识点介绍三:哈希算法详解
stm32·嵌入式硬件·哈希算法