五大数据结构02_哈希hash


看清楚
在这里插入图片描述
哈希 形如 key={ {field1,value1},...{fieldN,valueN} }

Redis hash 是一个string类型的field和value的映射表,它的添加、删除操作都是O(1)(平均)。hash特别适用于存储对象,将一个对象存储在hash类型中会占用更少的内存,并且可以方便的存取整个对象。

命令

常用命令

1、hset key field value
此外 Redis 提供了 hsetnx 命令,它们的关系就像 set 和 setnx 命令一样,只不过 作用域 由 键 变为 field。
2、hget key field
3、hdel key field [field …]
hdel 会删除 一个或多个 field,返回结果为 成功删除 field 的个数,
4、hlen key 计算field个数
5、批量设置或获取field-value

hmget key field [field ...]
hmset key field value [field value ...]

例如:

127.0.0.1:6379> hmset user:1 name tom age 12 city chengdu
OK
127.0.0.1:6379> hmget user:1 name city
1) "tom"
2) "chengdu"

6、判断field是否存在 hexists key field
存在 返回结果为 1,不包含时返回 0
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

9、 获取所有的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) "chengdu"

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

不常用命令

1、键值自增

hincrby key field
hincrbyfloat key field

hincrby 和 hincrbyfloat,就像 incrby 和 incrbyfloat 命令一样,但是它们的 作用域 是 field。
2、计算value的字符串长度

hstrlen key field

例如 hget user:1 name 的 value 是 tom,那么 hstrlen 的返回结果是 3。

127.0.0.1:6379> hstrlen user:1 name
(integer) 3

下面是 哈希类型命令 的 时间复杂度,开发人员可以参考此表选择适合的命令。
在这里插入图片描述

在这里插入图片描述

2. 内部编码

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

2.1. ziplist(压缩列表)更省内存

当 哈希类型 元素个数 小于
hash-max-ziplist-entries 配置(默认 512个)、 同时 所有值 都 小于
hash-max-ziplist-value 配置(默认 64 字节)时,Redis 会使用 ziplist 作为 哈希 的 内部实现,ziplist 使用更加 紧凑的结构 实现多个元素的 连续存储,所以在 节省内存 方面比 hashtable 更加优秀。

2.2. 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"

3. 适用场景

如图所示,为 关系型数据表 的两条 用户信息,用户的属性作为表的列,每条用户信息作为行。
在这里插入图片描述
使用 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;
}

3.1. 哈希结构与关系型表

需要注意的是 哈希类型 和 关系型数据库 有两点不同之处:

哈希类型 是 稀疏的,而 关系型数据库 是 完全结构化的,例如 哈希类型 每个 键 可以有不同的 field,为null的field可以不保留。而 关系型数据库 一旦添加新的 列,所有行 都要为其 设置值(即使为 NULL),如图所示:

在这里插入图片描述
关系型数据库 可以做复杂的 关系查询,而使用 Redis 去模拟关系型复杂查询 开发困难,维护成本高。

3.2. 几种缓存方式

到目前为止,我们已经能够用 三种方法 缓存 用户信息,下面给出三种方案的 实现方法 和 优缺点分析。
3.2.1. 原生字符串类型
给用户信息的每一个属性分配 一个键。

set user:1:name tom
set user:1:age 23
set user:1:city beijing

优点:简单直观,每个属性都支持 更新操作。
缺点:占用 过多的键,内存占用量 较大,同时用户信息 内聚性比较差,所以此种方案一般不会在生产环境使用。

3.2.2. 序列化字符串类型
将用户信息 序列化 后用 一个键 保存。

set user:1 serialize(userInfo)

优点:简化编程,如果合理的使用 序列化 可以 提高内存利用率。
缺点:序列化 和 反序列化 有一定的开销,同时每次 更新属性 都需要把 全部数据 取出进行 反序列化,更新后 再 序列化 到 Redis 中。

3.2.3. 哈希类型
每个用户属性使用 一对 field-value,但是只用 一个键 保存。

hmset user:1 name tom age 23 city beijing

优点:简单直观,如果使用合理可以 减少内存空间 的使用。
缺点:要控制和减少 哈希 在 ziplist 和 hashtable 两种 内部编码 的 转换,hashtable 会消耗 更多内存。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值