前言

相关介绍主要围绕着如下的一些常用的命令, 来看看 hash 相关操作的具体 api 

如下常用的命令来自于我们常见的教程 : https://www.runoob.com/redis/redis-hashes.html 

本文的相关代码 拷贝自 redis-6.2.0  

代码来自于 https://redis.io/ 

数据存储

当数据元素小于等于 hash_max_ziplist_entries(默认为512) 的时候使用的是 ziplist 来存储数据 

超过了 hash_max_ziplist_entries 之后, 数据结构更新为 dict 

HDEL key field1 [field2] - 执行 hdel user name age

13 hash 相关操作_客户端

这里可以看出的是 参数的数量要求大于等于 3 个, 也就是可以删除同一个 hash 的多个 key 

我们来看一下 hdelCommand 

获取 key 对应的 entry, 并确保类型为 HASH, 返回给客户端错误信息 

遍历 fieldList, 从 entry 里面删除 field 的数据 

如果当前 entry 有改动, 发送 db key 的更新通知 

返回给客户端的是 当前 entry 中删除的 key 的数据 

13 hash 相关操作_ziplist_02

具体的处理删除 field 的方法如下 

如果 encoding 为 ZIPLIST, 首先找到 field 对应的位置, 删除 field & fieldValue 

如果 encoding 为 HASHTABLE, 从 dict 里面删除 field 对应的 entry  

13 hash 相关操作_redis_03

最终客户端这边展示的结果如下, 返回的是 user 这个 hash 下面删除的元素的数量, 这里删除了 name -> jerry, age -> 77 两个元素  

13 hash 相关操作_客户端_04

HEXISTS key field - 执行 hexists user name 

13 hash 相关操作_客户端_05

这里可以看出的是 参数的数量要求等于 3 个 

我们来看一下 hexistsCommand 

获取 key 对应的 entry 

判断给定的 field 在 entry 中是否存在 

o 的类型为 HASH, encoding 为 ZIPLIST, ptr 指向了一个 ziplist 

13 hash 相关操作_hash_06

具体的业务处理的方式为 

如果是 ziplist, 获取 field 对应的元素信息, 如果存在 返回 1 

如果是 hashtable, 获取 field 对应的 entry 的信息, 如果存在 返回 1  

13 hash 相关操作_ziplist_07

最终客户端这边展示的结果如下, 返回的是 user 这个 hash 下面 name 这个 field 是否存在 

13 hash 相关操作_客户端_08

HGET key field - 执行 hget user name

13 hash 相关操作_ziplist_09

这里可以看出的是 参数的数量要求等于 3 个 

我们来看下 hgetCommand 

获取 key 对应的 entry 

获取给定的 field 对应的值 

13 hash 相关操作_hash_10

具体的业务处理的方式为 

如果是 ziplist, 获取 field 对应的元素信息, 返回 field 紧接着的 fieldValue 

如果是 hashtable, 获取 field 对应的 entry 的 value 返回 

这里获取到的是 len 为 5, 数据为 jerrry 

13 hash 相关操作_ziplist_11

最终客户端这边展示的结果如下, 返回的是 user 这个 hash 下面 name 这个 field 对应的值 

13 hash 相关操作_ziplist_12

HGETALL key - 执行 hgetall user

13 hash 相关操作_ziplist_13

这里可以看出的是 参数的数量要求等于 2 个 

我们来看一下 hgetallCommand 

响应元素长度给客户端 

遍历给定的 entry, 响应 key, value 给客户端 

13 hash 相关操作_hash_14

哈希的具体的迭代方式 

创建迭代器, 封装 subject, encoding, 如果是 hashtable, 获取迭代器

迭代数据, 如果 ziplist 编码, 基于 ziplistNext 进行迭代, 如果是 hashtable 编码, 基于 dictNext 进行迭代 

13 hash 相关操作_hash_15

最终客户端这边展示的结果如下, 返回的是 user 这个 hash 下面 field & fieldValue 列表 

13 hash 相关操作_dict_16

HINCRBY key field increment - 执行 hincrby user counter 2

13 hash 相关操作_客户端_17

这里可以看出的是 参数的数量要求等于 4 个 

我们来看一下 hincrbyCommand 

这里的相关操作 和字符串的 incr 的相关业务是相同的, 这里不多赘述 

13 hash 相关操作_redis_18

最终客户端这边展示的结果如下, user 这个 entry 下面 counter 不存在, 这里累增了 2, 返回的是 counter 累增之后的结果 2 

13 hash 相关操作_ziplist_19

HKEYS key - 执行 hkeys user

13 hash 相关操作_dict_20

这里可以看出的是 参数的数量要求等于 2 个 

我们来看一下 hkeysCommand 

同样是类似于上面的 hgetall user 

13 hash 相关操作_ziplist_21

最终客户端这边展示的结果如下, user 这个 entry 下面 所有的 key 列表 

13 hash 相关操作_hash_22

HLEN key - 执行 hlen user

13 hash 相关操作_redis_23

这里可以看出的是 参数的数量要求等于 2 个 

我们来看一下 hlenCommand 

获取到 key 对应的 entry 

获取 entry 里面元素的数量返回 

13 hash 相关操作_dict_24

元素个数的具体计算方式 

如果编码是 ziplist, 获取 ziplist 的长度 / 2, 即为哈希中元素的数量 

如果编码是 hashtable, 通过 hashtable 相关 元数据 直接获取元素数量 

13 hash 相关操作_客户端_25

最终客户端这边展示的结果如下, user 这个 entry 下面元素的数量 

13 hash 相关操作_dict_26

HMGET key field1 [field2]

13 hash 相关操作_dict_27

这里可以看出的是 参数的数量要求大于等于 3 个 

我们来看一下 hmgetCommand 

获取到 key 对应的 entry 

先返回一个结果长度 

再返回 fieldList 对应的所有的 value 列表 

获取 field 对应的 value 的方式就类似于 hget 里面的业务处理了 

13 hash 相关操作_客户端_28

最终客户端这边展示的结果如下, user 这个 entry 下面 name, age 对应的 value 列表 

13 hash 相关操作_hash_29

HMSET key field1 value1 [field2 value2] - 执行 hmset user field1 value1 field2 value2

13 hash 相关操作_hash_30

这里可以看出的是 参数的数量要求大于等于 4 个 

我们来看一下 hsetCommand 

需要确认 field, value 是成对出现 

然后 尝试更新存储的数据结构的转换 

循环 fieldList, valueList, 循环设置 field, value 

返回给客户端 创建成功 或者 具体设置的 field, value 对的数量 

发送 db key 的更新通知, 更新 dirty 

13 hash 相关操作_ziplist_31

设置 field, value 的具体的信息 

如果编码是 ziplist, 查询 field 是否存在, 如果是存在 更新 value, 否则 新增 field, value 

如果编码是 hashtable, 查询 field 对应的 entry 是否存在, 如果是存在 更新 value, 否则 新增 field, value 

13 hash 相关操作_客户端_32

最终客户端这边展示的结果如下, user 这个 entry 下面 设置 field1, field2 成功 

13 hash 相关操作_ziplist_33

HSET key field value 

13 hash 相关操作_客户端_34

这里可以看出的是 参数的数量要求大于等于 4 个 

hset, hsetnx 都是差不多, 这里只介绍一个 

这里的具体的实现 就和上面 hmset 的实现是同一套, 这不多赘述 

最终客户端这边展示的结果如下, user 这个 entry 下面 设置 field3, field4 成功 

13 hash 相关操作_hash_35

HVALS user

13 hash 相关操作_ziplist_36

这里可以看出的是 参数的数量要求大于等于 2 个 

我们看一下 hvalsCommand 

和上面 hgetall 的相关处理业务代码 基本一致, 这里不多赘述 

13 hash 相关操作_客户端_37

最终客户端这边展示的结果如下, user 这个 entry 下面所有的 value 列表 

13 hash 相关操作_redis_38

HSCAN key cursor [MATCH pattern] [COUNT count] - 执行 hscan user 0 MATCH * COUNT 1

13 hash 相关操作_redis_39

这里可以看出的是 参数的数量要求大于等于 2 个 

这的迭代类似于 11 key 相关操作 

不过对于这里编码为 ziplist 的情况 COUNT 这两个选项是没用的[这是一个很细的细节] 

迭代的是 ziplist 里面的所有的元素  

13 hash 相关操作_ziplist_40

最终客户端这边展示的结果如下, user 这个 entry 下面所有的 匹配 pattern 的 field, value 列表 

13 hash 相关操作_hash_41

完