内容简介:大部分编程语言都提供了哈希形如哈希类型中的
大部分编程语言都提供了 哈希 ( hash )类型,它们的叫法可能是 哈希 、 字典 、 关联数组 。在 Redis 中, 哈希类型 是指键值本身又是一个 键值对结构 。
哈希形如 value={ {field1,value1},...{fieldN,valueN} } , Redis 键值对 和 哈希类型 二者的关系如图所示:
哈希类型中的 映射关系 叫作 field-value ,这里的 value 是指 field 对应的 值 ,不是 键 对应的值。
正文
1. 相关命令
1.1. 基本命令
1.1.1. 设置值
hset key field value
下面为 user:1 添加一对 field-value ,如果设置成功会返回 1 ,反之会返回 0 。
127.0.0.1:6379> hset user:1 name tom (integer) 1 复制代码
此外 Redis 提供了 hsetnx 命令,它们的关系就像 set 和 setnx 命令一样,只不过 作用域 由 键 变为 field 。
1.1.2. 获取值
hget key field
下面操作用于获取 user:1 的 name 域(属性) 对应的值。
127.0.0.1:6379> hget user:1 name "tom" 复制代码
如果 键 或 field 不存在,会返回 nil :
127.0.0.1:6379> hget user:2 name (nil) 127.0.0.1:6379> hget user:1 age (nil) 复制代码
1.1.3. 删除field
hdel key field [field ...]
hdel 会删除 一个或多个 field ,返回结果为 成功删除 field 的个数,例如:
127.0.0.1:6379> hdel user:1 name (integer) 1 127.0.0.1:6379> hdel user:1 age (integer) 0 复制代码
1.1.4. 计算field个数
hlen key
例如键 user:1 有 3 个 field :
127.0.0.1:6379> hset user:1 name tom (integer) 1 127.0.0.1:6379> hset user:1 age 23 (integer) 1 127.0.0.1:6379> hset user:1 city chengdu (integer) 1 127.0.0.1:6379> hlen user:1 (integer) 3 复制代码
1.1.5. 批量设置或获取field-value
hmget key field [field ...] hmset key field value [field value ...]
hmset 和 hmget 分别是 批量设置 和 获取 field-value , hmset 需要的参数是 key 和 多对 field-value , hmget 需要的参数是 key 和 多个 field 。例如:
127.0.0.1:6379> hmset user:1 name tom age 12 city chengdu OK 127.0.0.1:6379> hmget user:1 name city 1) "tom" 2) "chengdu" 复制代码
1.1.6. 判断field是否存在
hexists key field
例如 user:1 包含 name 域,所以返回结果为 1 ,不包含时返回 0 :
127.0.0.1:6379> hexists user:1 name (integer) 1 复制代码
1.1.7. 获取所有field
hkeys key
hkeys 命令应该叫 hfields 更为恰当,它返回指定 哈希键 所有的 field ,例如:
127.0.0.1:6379> hkeys user:1 1) "name" 2) "age" 3) "city" 复制代码
1.1.8. 获取所有value
hvals key
下面操作获取 user:1 的全部 value :
127.0.0.1:6379> hvals user:1 1) "tom" 2) "12" 3) "chengdu" 复制代码
1.1.9. 获取所有的field-value
hgetall key
下面操作获取 user:1 所有的 field-value :
127.0.0.1:6379> hgetall user:1 1) "name" 2) "tom" 3) "age" 4) "12" 5) "city" 6) "chengdu" 复制代码
在使用 hgetall 时,如果 哈希元素 个数比较多,会存在 阻塞 Redis 的可能。如果开发人员只需要获取 部分 field ,可以使用 hmget ,如果一定要获取 全部 field-value ,可以使用 hscan 命令,该命令会 渐进式遍历 哈希类型。
1.2. 不常用命令
1.2.1. 键值自增
hincrby key field hincrbyfloat key field
hincrby 和 hincrbyfloat ,就像 incrby 和 incrbyfloat 命令一样,但是它们的 作用域 是 field 。
1.2.2. 计算value的字符串长度
hstrlen key field
例如 hget user:1 name 的 value 是 tom ,那么 hstrlen 的返回结果是 3 。
127.0.0.1:6379> hstrlen user:1 name (integer) 3 复制代码
下面是 哈希类型命令 的 时间复杂度 ,开发人员可以参考此表选择适合的命令。
2. 内部编码
哈希类型的 内部编码 有两种:
2.1. ziplist(压缩列表)
当 哈希类型 元素个数 小于 hash-max-ziplist-entries 配置(默认 512 个)、同时 所有值 都 小于 hash-max-ziplist-value 配置(默认 64 字节)时, Redis 会使用 ziplist 作为 哈希 的 内部实现 , ziplist 使用更加 紧凑的结构 实现多个元素的 连续存储 ,所以在 节省内存 方面比 hashtable 更加优秀。
2.2. hashtable(哈希表)
当 哈希类型 无法满足 ziplist 的条件时, Redis 会使用 hashtable 作为 哈希 的 内部实现 ,因为此时 ziplist 的 读写效率 会下降,而 hashtable 的读写 时间复杂度 为 O(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" 复制代码
- 当有
value大于64字节时, 内部编码 会由ziplist变为hashtable:
127.0.0.1:6379> hset hashkey f3 "one string is bigger than 64 byte...忽略..." OK 127.0.0.1:6379> object encoding hashkey "hashtable" 复制代码
- 当
field个数 超过512, 内部编码 也会由ziplist变为hashtable:
127.0.0.1:6379> hmset hashkey f1 v1 f2 v2 f3 v3 ... f513 v513 OK 127.0.0.1:6379> object encoding hashkey "hashtable" 复制代码
3. 适用场景
如图所示,为 关系型数据表 的两条 用户信息 ,用户的属性作为表的列,每条用户信息作为行。
使用 Redis 哈希结构 存储 用户信息 的示意图如下:
相比于使用 字符串序列化 缓存 用户信息 , 哈希类型 变得更加 直观 ,并且在 更新操作 上会 更加便捷 。可以将每个用户的 id 定义为 键后缀 ,多对 field-value 对应每个用户的 属性 ,类似如下伪代码:
public UserInfo getUserInfo(long id) {
// 用户id作为key后缀
String userRedisKey = "user:info:" + id;
// 使用hgetall获取所有用户信息映射关系
Object userInfoMap = redis.hgetAll(userRedisKey);
UserInfo userInfo;
if (userInfoMap != null) {
// 将映射关系转换为UserInfo
userInfo = transferMapToUserInfo(userInfoMap);
} else {
// 从 MySQL 中获取用户信息
userInfo = mysql.get(id);
// 将userInfo变为映射关系使用hmset保存到Redis中
redis.hmset(userRedisKey, transferUserInfoToMap(userInfo));
// 添加过期时间
redis.expire(userRedisKey, 3600);
}
return userInfo;
}
复制代码
3.1. 哈希结构与关系型表
需要注意的是 哈希类型 和 关系型数据库 有两点不同之处:
- 哈希类型 是 稀疏的 ,而 关系型数据库 是 完全结构化 的,例如 哈希类型 每个 键 可以有不同的
field,而 关系型数据库 一旦添加新的 列 , 所有行 都要为其 设置值 (即使为NULL),如图所示:
- 关系型数据库 可以做复杂的 关系查询 ,而使用
Redis去模拟关系型复杂查询 开发困难 , 维护成本高 。
3.2. 几种缓存方式
到目前为止,我们已经能够用 三种方法 缓存 用户信息 ,下面给出三种方案的 实现方法 和 优缺点分析 。
3.2.1. 原生字符串类型
给用户信息的每一个属性分配 一个键 。
set user:1:name tom set user:1:age 23 set user:1:city beijing 复制代码
-
优点:简单直观,每个属性都支持 更新操作 。
-
缺点:占用 过多的键 , 内存占用量 较大,同时用户信息 内聚性比较差 ,所以此种方案一般不会在生产环境使用。
3.2.2. 序列化字符串类型
将用户信息 序列化 后用 一个键 保存。
set user:1 serialize(userInfo) 复制代码
-
优点: 简化编程 ,如果合理的使用 序列化 可以 提高内存利用率 。
-
缺点: 序列化 和 反序列化 有一定的开销,同时每次 更新属性 都需要把 全部数据 取出进行 反序列化 , 更新后 再 序列化 到
Redis中。
3.2.3. 哈希类型
每个用户属性使用 一对 field-value ,但是只用 一个键 保存。
hmset user:1 name tom age 23 city beijing 复制代码
-
优点: 简单直观 ,如果使用合理可以 减少内存空间 的使用。
-
缺点:要控制和减少 哈希 在
ziplist和hashtable两种 内部编码 的 转换 ,hashtable会消耗 更多内存 。
小结
本文介绍了 Redis 中的 哈希结构 的 一些 基本命令 、 内部编码 和 适用场景 。最后对比了 关系型表 和 哈希结构 的区别,以及几种 存储方式 的优缺点。
以上所述就是小编给大家介绍的《深入剖析Redis系列(六) - Redis数据结构之哈希》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 深入剖析Redis系列(七) - Redis数据结构之列表
- 深入剖析Redis系列(八) - Redis数据结构之集合
- 深入剖析Redis系列(四) - Redis数据结构与全局命令概述
- 深入剖析Redis系列(五) - Redis数据结构之字符串
- kafka集群Producer基本数据结构及工作流程深入剖析-kafka 商业环境实战
- 【Java集合源码剖析】ArrayList源码剖析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
产品觉醒:产品经理的视角与方法论
判官(李泽澄) / 电子工业出版社 / 2018-11 / 59.80元
《产品觉醒:产品经理的视角与方法论》是作者多年工作经验的汇集,通过自己亲身经历来对产品运营、行业和人生选择做一个全面的复盘,为读者提供有一定深度的分析。 《产品觉醒:产品经理的视角与方法论》共7章,第1章介绍了产品经理应该具有的视角来分析和观察产品分析方法;第2章介绍了做产品时如何破局来解决相应的问题;第3章介绍了在做产品经理前先分析自己;第4章介绍了怎么来解决执行力的问题;第5章介绍了怎么......一起来看看 《产品觉醒:产品经理的视角与方法论》 这本书的介绍吧!
图片转BASE64编码
在线图片转Base64编码工具
RGB HSV 转换
RGB HSV 互转工具