Redis(6)—— 哈希

几乎所有的编程语言都提供了哈希( hash) 类型, 它们的叫法可能是哈希、 字典、 关联数组。 在Redis中, 哈希类型是指键值本身又是一个键值对结构, 形如value={{field1, value1}, ...{fieldN, valueN}}, Redis键值对和哈希类型二者的关系可以用图2-14来表示。

注意

哈希类型中的映射关系叫作field-value, 注意这里的value是指field对应的值, 不是键对应的值, 请注意value在不同上下文的作用。


2.3.1 命令
( 1) 设置值

hset key field value
下面为user: 1添加一对field-value:
127.0.0.1:6379> hset user:1 name tom
(integer) 1
如果设置成功会返回1, 反之会返回0。 此外Redis提供了hsetnx命令, 它们的关系就像set和setnx命令一样, 只不过作用域由键变为field。
( 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)
( 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
( 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 tianjin
(integer) 1
127.0.0.1:6379> hlen user:1
(integer) 3
( 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 mike age 12 city tianjin
OK
127.0.0.1:6379> hmget user:1 name city
1) "mike"
2) "tianjin"
( 6) 判断field是否存在
hexists key field
例如, user: 1包含name域, 所以返回结果为1, 不包含时返回0:
127.0.0.1:6379> hexists user:1 name
(integer) 1
( 7) 获取所有field
hkeys key
hkeys命令应该叫hfields更为恰当, 它返回指定哈希键所有的field, 例如:
127.0.0.1:6379> hkeys user:1
1) "name"
2) "age"
3) "city"
( 8) 获取所有value
hvals key
下面操作获取user: 1全部value:
127.0.0.1:6379> hvals user:1
1) "mike"
2) "12"
3) "tianjin"
( 9) 获取所有的field-value
hgetall key
下面操作获取user: 1所有的field-value:
127.0.0.1:6379> hgetall user:1
1) "name"
2) "mike"
3) "age"
4) "12"
5) "city"
6) "tianjin"


开发提示

在使用hgetall时, 如果哈希元素个数比较多, 会存在阻塞Redis的可能。如果开发人员只需要获取部分field, 可以使用hmget, 如果一定要获取全部field-value, 可以使用hscan命令, 该命令会渐进式遍历哈希类型, hscan将在2.7节介绍。


( 10) hincrby hincrbyfloat

hincrby key field
hincrbyfloat key field

hincrby和hincrbyfloat, 就像incrby和incrbyfloat命令一样, 但是它们的作用域是filed。


( 11) 计算value的字符串长度( 需要Redis3.2以上)
hstrlen key field
例如hget user: 1name的value是tom, 那么hstrlen的返回结果是3:
127.0.0.1:6379> hstrlen user:1 name
(integer) 3
表2-3是哈希类型命令的时间复杂度, 开发人员可以参考此表选择适合的命令。

2.3.2 内部编码

哈希类型的内部编码有两种:

·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.1) 当有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"
2.2) 当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"

有关哈希类型的内存优化技巧将在8.3节中详细介绍。

2.3.3 使用场景
图2-15为关系型数据表记录的两条用户信息, 用户的属性作为表的列,每条用户信息作为行。如果将其用哈希类型存储, 如图2-16所示。


相比于使用字符串序列化缓存用户信息, 哈希类型变得更加直观, 并且在更新操作上会更加便捷。 可以将每个用户的id定义为键后缀, 多对fieldvalue对应每个用户的属性, 类似如下伪代码:
UserInfo getUserInfo(long id){
    // 用户id作为key后缀
    userRedisKey = "user:info:" + id;
    // 使用hgetall获取所有用户信息映射关系
    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) , 如图2-17所示。
·关系型数据库可以做复杂的关系查询, 而Redis去模拟关系型复杂查询开发困难, 维护成本高。

开发人员需要将两者的特点搞清楚, 才能在适合的场景使用适合的技术。 到目前为止, 我们已经能够用三种方法缓存用户信息, 下面给出三种方案的实现方法和优缺点分析。
1) 原生字符串类型: 每个属性一个键。
set user:1:name tom
set user:1:age 23
set user:1:city beijing
优点: 简单直观, 每个属性都支持更新操作。
缺点: 占用过多的键, 内存占用量较大, 同时用户信息内聚性比较差,所以此种方案一般不会在生产环境使用。
2) 序列化字符串类型: 将用户信息序列化后用一个键保存。
set user:1 serialize(userInfo)
优点: 简化编程, 如果合理的使用序列化可以提高内存的使用效率。
缺点: 序列化和反序列化有一定的开销, 同时每次更新属性都需要把全部数据取出进行反序列化, 更新后再序列化到Redis中。
3) 哈希类型: 每个用户属性使用一对field-value, 但是只用一个键保存。
hmset user:1 name tomage 23 city beijing
优点: 简单直观, 如果使用合理可以减少内存空间的使用。
缺点: 要控制哈希在ziplist和hashtable两种内部编码的转换, hashtable会消耗更多内存
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值