企业级Redis开发运维从入门到实践 (5)— hash数据结构的内部编码及相关命令详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zx711166/article/details/82734333

哈希的内部编码

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

  • 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:
127.0.0.1:6379> hmset user:2 name kebi age 26
OK
127.0.0.1:6379> object encoding user:2
"ziplist"
当有value大于64个字节,内部编码会由ziplist变为hashtable:
127.0.0.1:6379> hmset user:1 info "沐春风,惹一身红尘;望秋月,化半缕轻烟。顾盼间乾坤倒转,一霎时沧海桑田。方晓,弹指红颜老,刹那芳华逝。"
127.0.0.1:6379> object encoding user:1
"hashtable"
当field个数超过512,内部编码也会由ziplist变为hashtable:
...待插入内容...

注意:当一个哈希的编码由ziplist变为hashtable的时候,即使在替换掉所有值,它一直都会是hashtable类型。

哈希键值结构

哈希键值结构还是一个key-value的结构,key永远都是字符串类型,value分为两个部分field、value;field代表这一个属性值,value代表一个值;可以单独进行更新、添加、删除。

例如下图key是用户编码,value是用户属性和属性对应的值。
这里写图片描述

特点

  • 结构与Map中value是Map结构相同,Map< String,Map< String,Object > >。
  • 可以简单认为是个“small redis”。
  • field不能相同,value可以相同。

哈希类型的相关命令:

hget、hset、hdel
  • hget key field:获取hash key对应的field的value,时间复杂度O(1)。
  • hset key field value:设置hash key对应field的value,时间复杂度O(1)。
  • hdel key field:删除hash key对应field的value,时间复杂度O(1)。
redis> hset user:1:info age 23
(integer) 1
redis> hget user:1:info age
"23"
redis> hset user:1:info name ronaldo
(integer) 1
redis> hgetall user:1:info
1) "age"
2) "23"
3) "name"
4) "ronaldo"
redis> hdel user:1:info age
(integer) 1
redis> hgetall user:1:info
1) "name"
2) "ronaldo"
hexists、hlen
  • hexists key field:判断hash key是否有field,时间复杂度O(1)。
  • hlen key:获取hash key field的数量,时间复杂度O(1)。
redis> hgetall user:1:info
1) "name"
2) "ronaldo"
3) "age"
4) "23"
redis> hexists user:1:info name
(integer) 1
redis> hlen user:1:info
(integer) 2
hmget、hmset
  • hmget key field1 field2 … fieldN:批量获取hash key的一批field对应的值,时间复杂度O(n)。
  • hmset key field1 value1 field2 value2 … fieldN valueN:批量设置hash key的一批field value,时间复杂度O(n)。
redis> hmset user:2:info age 30 name kaka page 50
OK
redis> hlen user:2:info
(integer) 3
redis> hmget user:2:info age name
1) "30"
2) "kaka"
实战
  • 实现如下功能:记录网站每个用户个人主页的访问量?
    解决:hincrby user:1:info pageview count
  • 实现如下功能:缓存视频的基本信息(数据源在mysql中)伪代码?
    解决:
//伪代码
public VideoInfo get(long id){
    String redis = redisPrefix + id;
    Map<String, String> hashMap = redis.hgetAll(redisKey);
    VideoInfo videoIndo = transferMapToVideo(hashMap);
    if(videoInfo == null){
        videoInfo = mysql.get(id);
        if(videoInfo != null){
            //序列化
            redis.hmset(redisKey, transferVideoToMap(videoInfo));
        }
    }
    return videoInfo;
}
hgetall、hvals、hkeys
  • hgetall key:返回hash key对应所有的field和value,时间复杂度O(n)。
  • hvals key:返回hash key对应所有field的value,,时间复杂度O(n)。
  • hkeys key:返回hash key对应所有field,时间复杂度O(n)。
redis> hgetall user:2:info
1) "age"
2) "30"
3) "name"
4) "kaka"
5) "page"
6) "50"
reids> hvals user:2:info
1) "30"
2) "kaka"
3) "50"
redis> hkeys user:2:info
1) "age"
2) "name"
3) "page"

:小心使用hgetall,hgetall会返回所有的field和value,假如hash key存入了很多属性,每次使用hgetall要注意了,它的速度会比较慢(单线程特点)。

string vs hash

相似的API

String Hash
get hget
set、setnx hset、hsetnx
del hdel
incr、incrby、decr、decrby hincrby
mset hmset
mget hmget
实战
  • 用户信息(String实现)
    一个key,对应一个完整的数据结构,每次取出、写入都需要重新进行序列化或者结构化。
    这里写图片描述

  • 用户信息(String实现-v2)
    数据分开多个key-value存放、方便对单条数据进行更新、写入、删除操作;但是用户信息不是整体不便于管理。
    这里写图片描述

  • 用户信息(hash)
    将用户的每个属性作为field,属性值作为value,设置在相应的用户key值中形成键值对存储;方便对单条数据进行更新、写入、删除操作,用户信息是整体也便于管理。
    这里写图片描述

  • 3种方案比较
    这里写图片描述

:hash的ttl不好控制,是ttl设置只能针对到每个key进行设置,没办法细化到每个field;只能在逻辑上进行操作来实现删除和管理。

hsetnx、hincrby、hincrbyfloat
  • hsetnx key field value:设置hash key对应field的value(如field已经存在,则失败),时间复杂度O(1)。
  • hincrby key field intCounter:hash key对应的field的value自增intCounter,时间复杂度O(1)。
  • hincrbyfloat key field floatCounter:hincrby浮点数版,时间复杂度O(1)。

猜你喜欢

转载自blog.csdn.net/zx711166/article/details/82734333