Redis中的Hash类型

Redis自身已经是键值对结构,Redis自身的键值对就是通过哈希的方式来组织的

把key这一层组织完成后,到了value这一层,calue其中的一种类型还可以再是哈希

后面的field-value是为了防止混淆前面的key

常用命令

hset

使用命令

返回值是设置成功的键值对(field-value)的个数 里面的value是字符串

hget

使用命令

如果要get某个value不存在的话会返回nil

hexists

返回值:1表示存在 0表示不存在

时间复杂度为O(1)

hdel

删除的field

使用命令

还可以批量删除

返回值是本次操作删除的字段个数

若是删除并不存在的field,则删除失败,影响行数为0

还可以删除所有的field

时间复杂度为O(1)

hkeys

这个操作,先根据key找到对应的hash,O(1),然后再遍历hash,O(K)

K为hash的元素个数

这个操作也是有一定风险的,和keys *是一样的风险

hvals

和hkeys相同,获得field相对应的value

h系列的命令必须要保证key对应的value必须是hash类型的

这个操作的时间复杂度为O(K)

这个操作的使用要慎重,有可能会阻塞住redis服务器

hgetall

获取hash中的所有字段以及对应的值

通用命令

每一个field对应value,这样的操作风险较大,多数情况下不需要查询所有的field,可能只查其中的几个field,所以引出hmget

hmget

类似于mget一次可以查询多个ield

  • 多个value对应的顺序和field一致,那么有没有hmset来一次设置多个hash的value,有!但是我们并不需要,hset就支持多个field-value的设置
  • 上述的hkeys,hvales,hgetall都是存在一定风险的,hash的元素个数太多,执行的耗时会比较长,从而阻塞redis,hscan遍历redis中的hash,但是他只属于”渐进式遍历“-敲一次遍历一小部分,在敲一次再遍历一小部分
  • 这让我们想到了ConcurrentHashMap,这个哈希表再扩容的时候,也是化整为零这样执行的

hlen

获取到hash中的所有字段的个数

时间复杂度为O(1),获取hash的个数,不需要遍历

hsetnx

这个操作和setnx类似,不存在时才能设置成功,如果存在则失败

hincrby

hash这里的value也可以当作数字来处理,hincrby 就可以加减整数,hincrbyfloat就可以加减小数,这里使用频率不是很高

hincrbyfloat

上述操作的时间复杂度为O(1)

Hash的命令小结

序号命令及描述
1HDEL key field1 [field2]
删除一个或多个哈希表字段
2HEXISTS key field
查看哈希表 key 中,指定的字段是否存在。
3HGET key field
获取存储在哈希表中指定字段的值。
4HGETALL key
获取在哈希表中指定 key 的所有字段和值
5HINCRBY key field increment
为哈希表 key 中的指定字段的整数值加上增量 increment 。
6HINCRBYFLOAT key field increment
为哈希表 key 中的指定字段的浮点数值加上增量 increment 。
7HKEYS key
获取哈希表中的所有字段
8HLEN key
获取哈希表中字段的数量
9HMGET key field1 [field2]
获取所有给定字段的值
10HMSET key field1 value1 [field2 value2 ]
同时将多个 field-value (域-值)对设置到哈希表 key 中。
11HSET key field value
将哈希表 key 中的字段 field 的值设为 value 。
12HSETNX key field value
只有在字段 field 不存在时,设置哈希表字段的值。
13HVALS key
获取哈希表中所有值。
14HSCAN key cursor [MATCH pattern] [COUNT count]
迭代哈希表中的键值对。

hash的内部编码:

ziplist(压缩列表)

zip,rar,gzip,7z.. 一些具体的压缩算法,压缩的本质是针对数据进行重新编码,不同的数据有不同的特点,结合这些特点,进行精妙的设计,重新编码之后,就能够缩小体积

例如abbcccddddeeeee可以压缩为1a2b3c4d

例如abcd00000000000000000000000000000efgh重新编码可以压缩为abcd[100]efgh

上述是比较粗糙的编码方式

ziplist内部的数据结构也是精心设计的,目的是节省内存空间

  • hash表示一个普通的hash表,可能会浪费一定的空间,hash首先是一个数组,数组上有些位置有元素,有些没元素
  • ziplist付出的代价,进行读写元素,速度是比较慢的,如果元素个数少,慢的并不明显,如果元素个数太多了,慢就会雪上加霜

如果

  1. 哈希中的元素个数比较少,使用ziplist表示,元素个数比较多,使用hashtable来表示
  2. 每个value的值长度都比较短,使用ziplist表示,如果某个value的长度太长,也会转换为hashtable
  • redis中有配置项可以改变转换的阈值:hash-max-ziplist-entries配置(默认512个)
  • 在etc/redis/redis.conf文件中可以看到配置项
  • 下面一行是表示单个value的长度不能超过64,不然转换为hashtable

应用场景

作为缓存

原生字符串类型-使用字符串类型,每个属性一个键,这种方式相当于把同一个数据的各个属性都分散出来了

string也是可以作为缓存使用的,存储结构化的数据时,使用hash类型更合适一些

转化为下图

上述类型使用string类型也能做到,就需要使用到json这样的格式

使用伪代码来描述一下获取信息或者执行信息

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;
}

缓存方式对比

  1. 如果使用string(json)的格式来表示UserInfo,万一只想获取其中的某个field,或者修改某个field就需要把整个json都读出来,解析成对象,操作field,再重新转成json格式,在写回去
  2. 如果使用hash的方式来表示UserInfo就可以使用field表示对象的某个属性(属性表的每个列)此时就可以非常方便的修改/获取任何一个属性的值了
  3. 使用hash的方式可能会导致内存的较大消耗(在ziplist和hashtable两种内部编码的转换)
  • 写代码要做到高内聚低耦合
  • 高内聚:有关联的代码写在一个模块里
  • 低耦合:避免牵一发动全身,这边一改bug,影响到了其他的地方

使用hash时要保证为null时也要填写上,如果使用string可以不填写,各有各的好

在下面的string存储缓存时,uid和key都是1,可不可以不存呢?

如果确实不想存uid也可以,但是在工程实践中,一般都会把uid在value中再存一份,再存一份,后续写到相关的代码,使用起来会比较方便

  • 26
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值