前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis:09---Hash对象

Redis:09---Hash对象

作者头像
用户3479834
发布2021-02-03 15:43:12
9030
发布2021-02-03 15:43:12
举报
文章被收录于专栏:游戏开发司机游戏开发司机

一、哈希对象简介

  • 几乎所有的编程语言都提供了哈希(hash)类型,它们的叫法可能是哈希、字典、关联数组
  • 哈希又称散列
  • 在Redis中,哈希类型是指键值本身又是一个键值对结构,形如value={{field1,value1},...{fieldN,valueN}},Redis键值对和哈希类型二者的关系可以下图表示
  • 一些特点:
    • 存储多个键值对之间的映射,并且键值对不允许重复
    • 在某一个固定的key中,其对应value中的field也不允许重复
    • 散列存储的值既可以是字符串也可以是数字值
    • 用户同样可以对散列存储的数字值执行自增操作或自减操作
    • 散列在很多方面是一个微缩版的Redis,不少字符串命令都有相应的散列版本
    • 熟悉文档数据库的读者可以将散列看作是文档数据库里面的文档,而熟悉关系数据库的读者可以将散列看作是关系数据库里面的行。因为“文档、行、散列”这三者都允许用户同时访问或修改一个或多个域
  • 注意:哈希类型中的映射关系叫作field-value,注意这里的value是指field对应的值,不是键对应的值,请注意value在不同上下文的作用。

二、命令

常用命令

  • hset:设置值。如果设置成功会返回1,反之会返回0

hset key field value

hsetnx:它们的关系就像set和setnx命令一样,只不过作用域由键变为field

  • hget:获取值。如果键或field不存在,返回nil

hget key field

好,到这里会有人好奇,这里到底是怎么样的结构,能不能直观的看到这些记录,还是我在前几篇文章说到的Redis DeskTop Manager可以查看

  • hdel:删除field
    • hdel会删除一个或多个field,返回结果为成功删除field的个数
    • 直到某一个key对应的field全部删除完全之后,该哈希对象才会被删除

hdel key field [field ...]

  • hlen:计算fileld个数
代码语言:javascript
复制
hlen key
  • hmget、hmset:批量获取/设置field-value
代码语言:javascript
复制
hmget key field [field ...]hmset key field value [field value ...]
  • hstrlen:计算value的字符串长度(需要Redis3.2以上)

hstrlen key field

其他命令

  • hincrby、hincrbyfloat:hincrby和hincrbyfloat,就像incrby和incrbyfloat命令一样,但是它们的作用域是filed
  • hexists:判断field是否存在。field存在返回1,不包含返回0
  • hkeys:获取所有field
代码语言:javascript
复制
hkeys key
  • hvals:获取所有值
代码语言:javascript
复制
hvals key
  • hgetall:获取所有的field-value
代码语言:javascript
复制
hgetall key
  • 提示:在使用hgetall时,如果哈希元素个数比较多,会存在阻塞Redis的可能。 如果开发人员只需要获取部分field,可以使用hmget,如果一定要获取全部 field-value,可以使用hscan命令,该命令会渐进式遍历哈希类型.
  • 下图给出了哈希类型命令的时间复杂度:

三、内部编码

  • 哈希类型的内部编码有两种:
    • ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries 配置(默认512个)、同时所有值都小于hash-max-ziplist-value配置(默认64 字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的 结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀
    • hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使 用hashtable作为哈希的内部实现,因为此时ziplist的读写效率会下降,而 hashtable的读写时间复杂度为O(1)

演示说明

  • 当field个数比较少且没有大的value时,内部编码为ziplist:
  • 当有value大于64字节,内部编码会由ziplist变为hashtable:
  • 当field个数超过512,内部编码也会由ziplist变为hashtable

四、字符串和散列的比较与选择

散列的优点

  • 散列的最大优势,只需要在数据库里面创建一个键,就可以把任意多的字段和值存储到散列里面

字符串的优点

  • 虽然散列键命令和字符串键命令在部分功能上有重合的地方,但是字符串键命令提供的操作比散列键命令更为丰富。比如,字符串能够使用 SETRANGE 命令和 GETRANGE 命令设置或者读取字符 串值的其中一部分,或者使用 APPEND 命令将新内容追加到字符串值的末尾,而散列键并不支持 这些操作
  • 再比如我们要设置键过期时间,键过期时间是针对整个键的,用户无法为散列中的不同字段设置不 同的过期时间,所以当一个散列键过期的时候,他包含的所有字段和值都会被删除。与此相反,如 果用户使用字符串键存储信息项,就不会遇到这样的问题——用户可以为每个字符串键分别设置不 同的过期时间,让它们根据实际的需要自动被删除

字符串和散列的选择

  • 使用场景对比:
    • 如果程序需要为单个数据项单独设置过期的时间,那么使用字符串键。
    • 如果程序需要对数据项执行诸如 SETRANGE、GETRANGE 或者 APPEND 等操作,那么优 先考虑使用字符串键。当然,用户也可以选择把数据存储在散列中,然后将类似 SETRANG E、GETRANGE 这样的操作交给客户端执行
    • 如果程序需要存储的数据项比较多,并且你希望尽可能地减少存储数据所需的内存,就应该优 先考虑使用散列键
    • 如果多个数据项在逻辑上属于同一组或者同一类,那么应该优先考虑使用散列键

五、使用场景

短网址生成程序

  • 此时我们可以根据该短链接查询到具体的源网址,并记录点击次数

存储信息

  • 下图为关系型数据表记录的两条用户信息,用户的属性作为表的列, 每条用户信息作为行
  • 如果将其用哈希类型存储,如下图所示:
  • 相比于使用字符串序列化缓存用户信息,哈希类型变得更加直观,并且在更新操作上会更加便捷。可以将每个用户的id定义为键后缀,多对fieldvalue对应每个用户的属性,类似如下伪代码:
代码语言:javascript
复制
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),如下图所示
    • 关系型数据库可以做复杂的关系查询,而Redis去模拟关系型复杂查询 开发困难,维护成本高

三种方案

  • 开发人员需要将两者的特点搞清楚,才能在适合的场景使用适合的技术。到目前为止,我们已经能够用三种方法缓存用户信息,下面给出三种方案的实现方法和优缺点分析
  • ①原生字符串类型:每个属性一个键
    • 优点:简单直观,每个属性都支持更新操作
    • 缺点:占用过多的键,内存占用量较大,同时用户信息内聚性比较差, 所以此种方案一般不会在生产环境使用
代码语言:javascript
复制
set user:1:name tomset user:1:age 23set user:1:city beijin
  • ②序列化字符串类型:将用户信息序列化后用一个键保存。
    • 优点:简化编程,如果合理的使用序列化可以提高内存的使用效率
    • 缺点:序列化和反序列化有一定的开销,同时每次更新属性都需要把全 部数据取出进行反序列化,更新后再序列化到Redis中
代码语言:javascript
复制
set user:1 serialize(userInfo)
  • ③哈希类型:每个用户属性使用一对field-value,但是只用一个键保存
    • 优点:简单直观,如果使用合理可以减少内存空间的使用
    • 缺点:要控制哈希在ziplist和hashtable两种内部编码的转换,hashtable会消耗更多内存

hmset user:1 name tomage 23 city beijing

不过对于我而言,我经常用到的是string,方便,快捷,一般是存储一些玩家的登录缓存信息,或者一些全服跨服战斗的相关数据

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 游戏开发司机 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、哈希对象简介
  • 二、命令
    • 常用命令
      • 其他命令
      • 三、内部编码
      • 四、字符串和散列的比较与选择
        • 散列的优点
          • 字符串的优点
            • 字符串和散列的选择
            • 五、使用场景
              • 短网址生成程序
                • 存储信息
                  • 三种方案
                  相关产品与服务
                  云数据库 Redis
                  腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档