redis · 29 3 月, 2021 0

REDIS数据结构-哈希对象(hash)

对象编码

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

ziplist

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

优点

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

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

hashtable

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

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

  • 字典中么个值都是一个字符串对象, 并保存键值对的值

编码转换

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

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

  • 哈希对象保存的键值对数量小于512个; 不能满足这两个条件的哈希对象需要使用hashtable

调整

  • hash-max-ziplist-value: 用于控制字符串能够设置的最大长度

  • hash-max-ziplist-entries: 用于控制压缩列表最大存储的节点数量

以上两个参数用于控制ziplisthashtable转换的条件.

# Hashes are encoded using a memory efficient data structure when they have a 
# small number of entries, and the biggest entry does not exceed a given 
# threshold. These thresholds can be configured using the following directives. 
hash-max-ziplist-entries 512 
hash-max-ziplist-value 64

哈希命令的实现

命令 ziplist编码实现方法 hashtable编码的实现方法
HSET 首先调用ziplistPush函数, 将键推入到压缩列表的表尾, 然后再次调用ziplistPush函数, 将值推入到压缩列表的表尾 调用dictAdd函数, 将新节点添加到字典里面
HGET 首先调用ziplistFind函数, 在压缩列表中查找指定键所对应的节点, 然后调用ziplistNext函数, 将指针移动到键节点叛变的值节点, 然后返回节点 调用dictFine行数, 在字典中查找给定键, 然后调用dictGetVal函数, 返回该键所对应的值.
HEXISTS 调用ziplistFind函数, 在压缩列表中查找指定键所对应的节点, 如果找到的话, 说明键值对存在, 没找到的话说明键值对不存在 调用dictFind函数, 在字典中查找给定键, 如果找到的话说明键值对存在, 没找到的话说明键值不存在
HDEL 调用ziplistFind函数, 在压缩列表中查找指定键所对应的节点, 然后将对应的键节点, 以及键节点旁边的值节点都删除掉 调用dectDelete函数, 将指定键所对应的键值对从字典中删除掉
HLEN 调用ziplistLen函数, 取的压缩列表包含节点的总数量, 将这个数量除以2, 得出的结果就是压缩列表保存的键值对的数量 调用dictSize函数, 返回字典包含的键值对的数量, 这个数量就是哈希对象包含的键值对的数量
HGETALL 遍历整个压缩列表, 用ziplistGet函数返回所有键和值 遍历真个字典, 用dictGetKey函数返回字典的键, 用dictGetVal函数返回字典的值