一、前言

原文解析

《闲扯Redis六》Redis五种数据类型之Hash型-LMLPHP

Redis 中的 hash 是我们经常使用到的一种数据类型,根据使用方式的不同,可以应用到很多场景中。

二、实现分析

 由上述结构图可知,Hash类型有以下两种实现方式:

1.ziplist 编码作为底层实现

ziplist 编码的哈希对象使用压缩列表作为底层实现, 每当有新的键值对要加入到哈希对象时, 程序会先将保存了键的压缩列表节点推入到压缩列表表尾, 然后再将保存了值的压缩列表节点推入到压缩列表表尾, 因此:

例如, 我们执行以下 HSET 命令, 那么服务器将创建一个列表对象作为 profile 键的值:

redis> HSET profile name "Tom"
(integer) 1

redis> HSET profile age 25
(integer) 1

redis> HSET profile career "Programmer"
(integer) 1

profile 键的值对象使用的是 ziplist 编码, 其中对象所使用的压缩列表结构如下图所示。

2.hashtable 编码作为底层实现

hashtable 编码的哈希对象使用字典作为底层实现, 哈希对象中的每个键值对都使用一个字典键值对来保存:

字典的每个键都是一个字符串对象, 对象中保存了键值对的键;

字典的每个值都是一个字符串对象, 对象中保存了键值对的值。

例如, 如果前面 profile 键创建的不是 ziplist 编码的哈希对象, 而是 hashtable 编码的哈希对象, 那么这个哈希对象结构如下图所示。

三、命令实现

因为哈希键的值为哈希对象, 所以用于哈希键的所有命令都是针对哈希对象来构建的, 下表列出了其中一部分哈希键命令, 以及这些命令在不同编码的哈希对象下的实现方法。

四、编码转换

当哈希对象可以同时满足以下两个条件时, 哈希对象使用 ziplist 编码:

哈希对象保存的所有键值对的键和值的字符串长度都小于 64 字节;

哈希对象保存的键值对数量小于 512 个;

不能满足这两个条件的哈希对象需要使用 hashtable 编码。

注意:这两个条件的上限值是可以修改的, 具体请看配置文件中关于 hash-max-ziplist-value 选项和 hash-max-ziplist-entries 选项的说明。

对于使用 ziplist 编码的列表对象来说, 当使用 ziplist 编码所需的两个条件的任意一个不能被满足时, 对象的编码转换操作就会被执行: 原本保存在压缩列表里的所有键值对都会被转移并保存到字典里面, 对象的编码也会从 ziplist 变为 hashtable 。

以下代码展示了哈希对象编码转换的情况:

1.键的长度太大引起编码转换

# 哈希对象只包含一个键和值都不超过 64 个字节的键值对
redis> HSET book name "Mastering C++ in 21 days"
(integer) 1

redis> OBJECT ENCODING book
"ziplist"

# 向哈希对象添加一个新的键值对,键的长度为 66 字节
redis> HSET book long_long_long_long_long_long_long_long_long_long_long_description "content"
(integer) 1

# 编码已改变
redis> OBJECT ENCODING book
"hashtable"

2.值的长度太大引起编码转换

# 哈希对象只包含一个键和值都不超过 64 个字节的键值对
redis> HSET blah greeting "hello world"
(integer) 1

redis> OBJECT ENCODING blah
"ziplist"

# 向哈希对象添加一个新的键值对,值的长度为 68 字节
redis> HSET blah story "many string ... many string ... many string ... many string ... many"
(integer) 1

# 编码已改变
redis> OBJECT ENCODING blah
"hashtable"

3.键值对数量过多引起编码转换

# 创建一个包含 512 个键值对的哈希对象
redis> EVAL "for i=1, 512 do redis.call('HSET', KEYS[1], i, i) end" 1 "numbers"
(nil)

redis> HLEN numbers
(integer) 512

redis> OBJECT ENCODING numbers
"ziplist"

# 再向哈希对象添加一个新的键值对,使得键值对的数量变成 513 个
redis> HMSET numbers "key" "value"
OK

redis> HLEN numbers
(integer) 513

# 编码改变
redis> OBJECT ENCODING numbers
"hashtable"

五、要点总结

《闲扯Redis六》Redis五种数据类型之Hash型-LMLPHP

07-23 17:57