redis hash原理redis底层数据结构

00-1010大多数编程语言都提供ffdxxmhash)类型,可以称为ffdxxm、字典和关联数组。在Redis中,ffdxxm类型意味着密钥本身是一个键值对结构。

ffdxxm的形状是value={ {field1,value1},{fieldN,valueen}},Redis键值对和ffdxxm类型的关系如图所示:

ffdxxm类型中的映射关系称为field-value,其中值指的是对应于字段的值,而不是对应于键的值。

前言

1.相关命令

1.1.基本命令

1.1.1.设定值

hset关键字字段值

接下来,为用户添加一对字段值:1。如果设置成功,则返回1,否则返回0。

127.0.0.1:6379 hset用户名user:1

整数)1

此外,Redis提供了hsetnx命令,它们的关系就像set和setnx命令一样,只是作用域从键变成了字段。

1.1.2.获取值

hget关键字字段

以下操作用于获取用户的名称字段属性)对应的值:1。

hget用户名3:6379

汤姆

如果键或字段不存在,它将返回零:

hget用户名3:6379

无)

hget用户年龄127 . 0 . 0 . 1:637901

无)

1.1.3.删除字段

hdel关键字段[字段.]

Hdel删除一个或多个字段,并返回成功删除的字段数,例如:

127.0.0.1:6379 hdel用户名:1

整数)1

127.0.0.1:6379 hdel用户年龄:1

整数)0

1.1.4.计算字段数量

海伦钥匙

例如,关键用户:1有三个字段:

127.0.0.1:6379 hset用户名user:1

整数)1

23岁的hset用户: 1:63379

整数)1

127.0.0.1:6379 hset用户:1成都市

整数)1

hlen用户127 . 0 . 0 . 1:637901

整数)3

1.1.5.批量设置或获取字段值

hmget关键字字段[字段.]

hmset键字段值[字段值.]

Hmset和hmget分别是批量设置和获取字段值。hmset需要的参数是键和多对字段值,hmget需要的参数是键和多字段。例如:

127.0.0.1:6379 hmset用户姓名tom年龄12岁城市成都

127 . 0 . 0 . 1:6379 hmget user :1 name city

1)“汤姆”

2)‘成都’

1.1.6.判断字段是否存在。

hexists关键字字段

例如,用户:1包含名称字段,所以返回结果为1;如果没有,则返回0:

127.0.0.1:6379用户名称:1

整数)1

1.1.7.获取所有字段

hkeys密钥

Hkeys命令应该更恰当地称为HFfield。它返回指定ffdxxm键的所有字段,例如:

127 . 0 . 0 . 1999年12月16日

1)“名称”

2)“年龄”

3)“城市”

1.1.8.获得所有价值

hvals键

以下操作获取用户的所有值:1:

hvals用户127 . 0 . 0 . 13360 . 133636363606

1)“汤姆”

2) ’12’

3)‘成都’

1.1

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

1.2. 不常用命令

1.2.1. 键值自增

hincrby key field

hincrbyfloat key field

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

1.2.2. 计算value的字符串长度

hstrlen key field

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

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

下面是 ffdxxm类型命令 的 时间复杂度,开发人员可以参考此表选择适合的命令。

2. 内部编码

ffdxxm类型 的 内部编码 有两种:

2.1. ziplist(压缩列表)

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

2.2. hashtable(ffdxxm表)

当 ffdxxm类型 无法满足 ziplist 的条件时,Redis 会使用 hashtable 作为 ffdxxm 的 内部实现,因为此时 ziplist 的 读写效率 会下降,而 hashtable 的读写 时间复杂度 为 O(1)。

下面的示例演示了 ffdxxm类型 的 内部编码,以及相应的变化。

当 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 ffdxxm结构 存储 用户信息 的示意图如下:

相比于使用 字符串序列化 缓存 用户信息,ffdxxm类型 变得更加 直观,并且在 更新操作 上会 更加便捷。可以将每个用户的 id 定义为 键后缀,多对 field-value 对应每个用户的 属性,类似如下伪代码:

public UserInfo getUserInfolong id) {
// 用户id作为key后缀
String userRedisKey = “user:info:” + id;
// 使用hgetall获取所有用户信息映射关系
Object userInfoMap = redis.hgetAlluserRedisKey);
UserInfo userInfo;
if userInfoMap != null) {
// 将映射关系转换为UserInfo
userInfo = transferMapToUserInfouserInfoMap);
} else {
// 从MySQL中获取用户信息
userInfo = mysql.getid);
// 将userInfo变为映射关系使用hmset保存到Redis中
redis.hmsetuserRedisKey, transferUserInfoToMapuserInfo));
// 添加过期时间
redis.expireuserRedisKey, 3600);
}
return userInfo;
}

3.1. ffdxxm结构与关系型表

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

ffdxxm类型 是 稀疏的,而 关系型数据库 是 完全结构化的,例如 ffdxxm类型 每个 键 可以有不同的 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 serializeuserInfo)
优点:简化编程,如果合理的使用 序列化 可以 提高内存利用率。缺点:序列化 和 反序列化 有一定的开销,同时每次 更新属性 都需要把 全部数据 取出进行 反序列化,更新后 再 序列化 到 Redis 中。

3.2.3. ffdxxm类型

每个用户属性使用 一对 field-value,但是只用 一个键 保存。

hmset user:1 name tom age 23 city beijing
优点:简单直观,如果使用合理可以 减少内存空间 的使用。缺点:要控制和减少 ffdxxm 在 ziplist 和 hashtable 两种 内部编码 的 转换,hashtable 会消耗 更多内存。

小结

本文介绍了 Redis 中的 ffdxxm结构 的 一些 基本命令、内部编码 和 适用场景。最后对比了 关系型表 和 ffdxxm结构 的区别,以及几种 存储方式 的优缺点。

参考

《Redis 开发与运维》

快三大小单双稳赚买法列,每条用户信息作为行。

使用 Redis ffdxxm结构 存储 用户信息 的示意图如下:

相比于使用 字符串序列化 缓存 用户信息,ffdxxm类型 变得更加 直观,并且在 更新操作 上会 更加便捷。可以将每个用户的 id 定义为 键后缀,多对 field-value 对应每个用户的 属性,类似如下伪代码:

public UserInfo getUserInfolong id) {
// 用户id作为key后缀
String userRedisKey = “user:info:” + id;
// 使用hgetall获取所有用户信息映射关系
Object userInfoMap = redis.hgetAlluserRedisKey);
UserInfo userInfo;
if userInfoMap != null) {
// 将映射关系转换为UserInfo
userInfo = transferMapToUserInfouserInfoMap);
} else {
// 从MySQL中获取用户信息
userInfo = mysql.getid);
// 将userInfo变为映射关系使用hmset保存到Redis中
redis.hmsetuserRedisKey, transferUserInfoToMapuserInfo));
// 添加过期时间
redis.expireuserRedisKey, 3600);
}
return userInfo;
}

3.1. ffdxxm结构与关系型表

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

ffdxxm类型 是 稀疏的,而 关系型数据库 是 完全结构化的,例如 ffdxxm类型 每个 键 可以有不同的 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 serializeuserInfo)
优点:简化编程,如果合理的使用 序列化 可以 提高内存利用率。缺点:序列化 和 反序列化 有一定的开销,同时每次 更新属性 都需要把 全部数据 取出进行 反序列化,更新后 再 序列化 到 Redis 中。

3.2.3. ffdxxm类型

每个用户属性使用 一对 field-value,但是只用 一个键 保存。

hmset user:1 name tom age 23 city beijing
优点:简单直观,如果使用合理可以 减少内存空间 的使用。缺点:要控制和减少 ffdxxm 在 ziplist 和 hashtable 两种 内部编码 的 转换,hashtable 会消耗 更多内存。

小结

本文介绍了 Redis 中的 ffdxxm结构 的 一些 基本命令、内部编码 和 适用场景。最后对比了 关系型表 和 ffdxxm结构 的区别,以及几种 存储方式 的优缺点。

参考

《Redis 开发与运维》

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注