Redis阅读笔记-哈希对象

Redis阅读笔记-哈希对象

编码

​ 哈希对象的编码可以是ziplist或hashtable。

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

  • 保存了同一键值对的两个节点总数紧挨在一起, 保存键的节点在前,保存值得节点在后

  • 先添加到哈希对象中得键值对会被放在压缩列表得表头方向, 而后来添加到哈希对象中得键值对会被放在压缩列表得表尾方向。

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

    127.0.0.1:6379> hset profile name 'Tom'
    (integer) 1
    127.0.0.1:6379> hset profile age 25
    (integer) 1
    127.0.0.1:6379> hset profile career 'Programmer'
    (integer) 1
    

    ​ 若profile键的值对象使用的是ziplist编码, 那么这个值对象将会是下图的所示:

    redisObject
    type
    REDIS_HASH
    encoding
    REDIS_ENCODING_ZIPLIST
    ptr 压缩列表
    ...

    其中对象所有的压缩列表如下图所示:

    zlbytes zltail zllen "name" "Tom" "age" 25 "career" "Programmer" zlend

    第一个添加键值对的键

    第一个添加键值对的值

    第二个添加键值对的键

    第二个添加键值对的值

    第三个添加键值对的键

    第三个添加键值对的值

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

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

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

​ 举个例子, 如果前面的profile键创建的不是ziplist编码的哈希对象, 而是hashtable编码的哈希对象,那么这个哈希对象应该是下图所示:

redisObject
type
REDIS_HASH
encoding
REDIS_ENCODING_HT
ptr dict
... StringObject
"age"
StringObject
25
StringObject
"career"
StringObject
"Programmer"
StringObject
"name"
StringObject
"Tom"

编码转换

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

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

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

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

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

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

​ 以下代码展示了哈希对象因为键值对的键长度太大而引起的编码转换情况:

# 哈希对象64只包含一个键和值都不超过64个字节的键值对
127.0.0.1:6379> hset book name "Mastering C++ in 21 days"
(integer) 1
127.0.0.1:6379> object encoding book
"ziplist"

# 向哈希对象中添加一个新的键值对, 键的长度为66个字节
127.0.0.1:6379> hset book long_long_long_long_long_long_long_long_long_long_long_descriptio n "content"
(integer) 1 

# 编码已改变
127.0.0.1:6379> object encoding book
"hashtable"

​ 除了键的长度太大会引起编码转换外, 值得长度太大也会引起编码转换, 以下代码展示了这个情况的一个实例:

# 哈希对象只包含一个键和值都不超过64个字节的键值对
127.0.0.1:6379> hset blah greeting 'hello world'
(integer) 1
127.0.0.1:6379> object encoding blah

# 向哈希对象添加一个新的键值对,值的长度为68
127.0.0.1:6379> hset blah story "many string ... many string ... many string ... many string ... many" 
(integer) 1

# 编码已改变
127.0.0.1:6379》 object encoding blah
"hashtable"

​ 最后,以下代码展示了哈希对象因为包含键值对的数量过多而引起编码转换的情况:

# 创建一个包含512个键值对的哈希对象
127.0.0.1:6379> eval "for i = 1, 512 do redis.call('HSET', KEYS[1], i, i)end" 1 "numbers"
(nil)
127.0.0.1:6379> hlen numbers
(integer) 512
127.0.0.1:6379> object encoding numbers
"ziplist"
127.0.0.1:6379> hmset numbers "key" "value"
OK

# 再向哈希对象添加一个新的键值对, 使得键值对数量超过513个
127.0.0.1:6379> hlen numbers
(integer) 513

# 编码已改变
127.0.0.1:6379> object encoding numbers
"hashtable"

posted @ 2020-10-09 17:31  phper-liunian  阅读(67)  评论(0编辑  收藏  举报