Redis Hash哈希 常用命令、内部编码 及 使用场景

前言

在Redis中,哈希类型是指值本身又是一个键值对结构
形如 key=“key”,value={{ field1, value1 }, …, {fieldN, valueN } },
Redis 键值对和哈希类型二者的关系可以用下图关系表示

  • 字符串和哈希类型对比
    在这里插入图片描述

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


常用命令

HSET

HSET key field value [field value ...]

设置hash中指定的字段(field)的值(value)。
时间复杂度:插入一组field为O(1),插入N组field为O(N)
返回值:添加的字段的个数。

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

HGET

HGET key field

获取hash中指定字段的值。
时间复杂度:O(1)
返回值:字段对应的 值 或者 nil。

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

HEXISTS

HEXISTS key field

判断hash中是否有指定的字段。
时间复杂度:O(1)
返回值:1 表示存在,0 表示不存在。

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

HDEL

HDEL key field [field ...]

删除hash中指定的字段。
时间复杂度:删除一组field为O(1),删除N组field为O(N)
返回值:本次操作删除的字段个数。

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

HKEYS

HKEYS key

获取hash中的所有字段
时间复杂度: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"

HVALS

HVALS key

获取hash中的所有的
时间复杂度: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"


HGETALL

HGETALL key

获取hash中的所有字段以及对应的
时间复杂度: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"

HMGET

HMGET key field [field ...]

一次获取hash中 多个 字段的值。
时间复杂度:只查询一个元素为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 命令,该命令采用渐进式遍历哈希类型


HLEN

HLEN key

获取hash中的所有字段的个数
时间复杂度:O(1)
返回值:字段个数。

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

HSETNX

HSETNX key field value

在字段不存在的情况下,设置hash中的字段和值
时间复杂度:O(1)
返回值:1 表示设置成功,0 表示失败。

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

HINCRBY

HINCRBY key field increment

将hash中 字段对应的数值 添加 指定的值
时间复杂度: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

HINCRBYFLOAT

HINCRBYFLOAT key field increment

HINCRBY的浮点数版本
时间复杂度: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"

命令小结

命令执行效果时间复杂度
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-value
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)
hincrbyfloat key 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 "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"

使用场景

图1 为关系型数据表记录的两条用户信息,用户的属性表现为表的列,每条用户信息表现为行。
如果映射关系表示这两个用户信息,则如图2所示。

图1 关系型数据表保存用户信息
在这里插入图片描述

图2 映射关系表示用户信息
在这里插入图片描述

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

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,如下图 所示
  • 关系数据库可以做复杂的关系查询,而 Redis 去模拟关系型复杂查询,例如联表查询、聚合查询等基本不可能,维护成本高。

关系型数据库稀疏性
在这里插入图片描述


缓存芳式对比

  • 原生字符串类型 ⸺ 使用字符串类型,每个属性一个键。
set user:1:name James
 set user:1:age 23
 set user:1:city Beijing
  • 优点:实现简单,针对个别属性变更也很灵活。
  • 缺点:占用过多的键,内存占用量较大,同时用户信息在 Redis 中比较分散,缺少内聚性,所以这种方案基本没有实用性。

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卑微的Coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值