赞
踩
本文来说下Redis数据结构之哈希
大部分编程语言都提供了 哈希(hash)类型,它们的叫法可能是 哈希、字典、关联数组。在 Redis 中,哈希类型 是指键值本身又是一个 键值对结构。
哈希 形如 value={ {field1,value1},…{fieldN,valueN} },Redis 键值对 和 哈希类型 二者的关系如图所示
哈希类型中的 映射关系 叫作 field-value,这里的 value 是指 field 对应的 值,不是 键 对应的值。
设置值:hset key field value
下面为 user:1 添加一对 field-value,如果设置成功会返回 1,反之会返回 0。
127.0.0.1:6379> hset user:1 name tom
(integer) 1
(1.89s)
此外 Redis 提供了 hsetnx 命令,它们的关系就像 set 和 setnx 命令一样,只不过 作用域 由 键 变为 field。
获取值: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:1 name
"tom"
127.0.0.1:6379> hget user:2 name
(nil)
127.0.0.1:6379> hget user:1 age
(nil)
127.0.0.1:6379>
删除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
127.0.0.1:6379>
计算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 24
(integer) 1
127.0.0.1:6379> hset user:1 city shanghai
(integer) 1
127.0.0.1:6379> hlen user:1
(integer) 3
127.0.0.1:6379>
批量设置或获取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 shanghai
OK
127.0.0.1:6379> hmget user:1 name city
1) "tom"
2) "shanghai"
127.0.0.1:6379>
判断field是否存在:hexists key field
例如 user:1 包含 name 域,所以返回结果为 1,不包含时返回 0:
127.0.0.1:6379> hexists user:1 name
(integer) 1
127.0.0.1:6379> hexists user:1 names
(integer) 0
127.0.0.1:6379>
获取所有field:hkeys key
hkeys 命令应该叫 hfields 更为恰当,它返回指定 哈希键 所有的 field,例如:
127.0.0.1:6379> hkeys user:1
1) "name"
2) "age"
3) "city"
127.0.0.1:6379>
获取所有value:hvals key
下面操作获取 user:1 的全部 value:
127.0.0.1:6379> hvals user:1
1) "tom"
2) "12"
3) "shanghai"
127.0.0.1:6379>
获取所有的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) "shanghai"
127.0.0.1:6379>
在使用 hgetall 时,如果 哈希元素 个数比较多,会存在 阻塞 Redis 的可能。如果开发人员只需要获取 部分 field,可以使用 hmget,如果一定要获取 全部 field-value,可以使用 hscan 命令,该命令会 渐进式遍历 哈希类型。
键值自增:hincrby key field hincrbyfloat key field
hincrby 和 hincrbyfloat,就像 incrby 和 incrbyfloat 命令一样,但是它们的 作用域 是 field。
计算value的字符串长度:hstrlen key field
例如 hget user:1 name 的 value 是 tom,那么 hstrlen 的返回结果是 3。
127.0.0.1:6379> hstrlen user:1 name
(integer) 3
下面是 哈希类型命令 的 时间复杂度,开发人员可以参考此表选择适合的命令。
哈希类型 的 内部编码 有两种:
当 哈希类型 元素个数 小于 hash-max-ziplist-entries 配置(默认 512 个)、同时 所有值 都 小于 hash-max-ziplist-value 配置(默认 64 字节)时,Redis 会使用 ziplist 作为 哈希 的 内部实现,ziplist 使用更加 紧凑的结构 实现多个元素的 连续存储,所以在 节省内存 方面比 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"
如图所示,为 关系型数据表 的两条 用户信息,用户的属性作为表的列,每条用户信息作为行。
使用 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; }
需要注意的是 哈希类型 和 关系型数据库 有两点不同之处:
哈希类型 是 稀疏的,而 关系型数据库 是 完全结构化的,例如 哈希类型 每个 键 可以有不同的 field,而 关系型数据库 一旦添加新的 列,所有行 都要为其 设置值(即使为 NULL),如图所示:
关系型数据库 可以做复杂的 关系查询,而使用 Redis 去模拟关系型复杂查询 开发困难,维护成本高。
到目前为止,我们已经能够用 三种方法 缓存 用户信息,下面给出三种方案的 实现方法 和 优缺点分析。
原生字符串类型
给用户信息的每一个属性分配 一个键。
set user:1:name tom
set user:1:age 23
set user:1:city beijing
序列化字符串类型
将用户信息 序列化 后用 一个键 保存。
set user:1 serialize(userInfo)
哈希类型
每个用户属性使用 一对 field-value,但是只用 一个键 保存。
hmset user:1 name tom age 23 city beijing
《Redis 开发与运维》
本文介绍了 Redis 中的 哈希结构 的 一些 基本命令、内部编码 和 适用场景。最后对比了 关系型表 和 哈希结构 的区别,以及几种 存储方式 的优缺点。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。