Redis 中的这种重新哈希的操作因为不是一次性全部 rehash,而是分多次来慢慢的将 ht[0] 中的键值对 rehash 到 ht[1],故而这种操作也称之为渐进式 rehash。渐进式 rehash 可以避免集中式 rehash 带来的庞大计算量,是一种分而治之的思想。
在渐进式 rehash 过程中,因为还可能会有新的键值对存进来,此时** Redis 的做法是新添加的键值对统一放入 ht[1] 中,这样就确保了 ht[0] 键值对的数量只会减少**。
当正在执行 rehash操作时,如果服务器收到来自客户端的命令请求操作,则会先查询 ht[0],查找不到结果再到ht[1] 中查询。
ziplist关于 ziplist 的一些特性,之前的文章中有单独进行过分析,想要详细了解的,可以点击这里。但是需要注意的是哈希对象中的 ziplist 和列表对象中 ziplist 的有一点不同就是哈希对象是一个 key-value 形式,所以其 ziplist 中也表现为 key-value,key 和 value 紧挨在一起:
ziplist 和 hashtable 的编码转换当一个哈希对象可以满足以下两个条件中的任意一个,哈希对象会选择使用 ziplist 编码来进行存储:
哈希对象中的所有键值对总长度(包括键和值)小于等于 64字节(这个阈值可以通过参数 hash-max-ziplist-value 来进行控制)。
哈希对象中的键值对数量小于等于 512 个(这个阈值可以通过参数 hash-max-ziplist-entries 来进行控制)。
一旦不满足这两个条件中的任意一个,哈希对象就会选择使用 hashtable 编码进行存储。
哈希对象常用命令hset key field value:设置单个 field(哈希对象的 key 值)。
hmset key field1 value1 field2 value2 :设置多个 field(哈希对象的 key 值)。
hsetnx key field value:将哈希表 key 中域 field 的值设置为 value,如果 field 已存在,则不执行任何操作。
hget key field:获取哈希表 key 中的域 field 对应的 value。
hmget key field1 field2:获取哈希表 key 中的多个域 field 对应的 value。
hdel key field1 field2:删除哈希表 key 中的一个或者多个 field。
hlen key:返回哈希表key中域的数量。
hincrby key field increment:为哈希表 key 中的域 field 的值加上增量 increment ,increment 可以为负数,如果 field 不是数字则会报错。
hincrbyfloat key field increment:为哈希表 key 中的域 field 的值加上增量 increment,increment 可以为负数,如果 field 不是 float 类型则会报错。
hkeys key:获取哈希表 key 中的所有域。
hvals key:获取哈希表中所有域的值。
了解了操作哈希对象的常用命令,我们就可以来验证下前面提到的哈希对象的类型和编码了,在测试之前为了防止其他 key 值的干扰,我们先执行 flushall 命令清空 Redis 数据库。
然后依次执行如下命令:
hset address country china type address object encoding address得到如下效果:
可以看到当我们的哈希对象中只有一个键值对的时候,底层编码是 ziplist。
现在我们将 hash-max-ziplist-entries 参数改成 2,然后重启 Redis,最后再输入如下命令进行测试:
hmset key field1 value1 field2 value2 field3 value3 object encoding key输出之后得到如下结果:
可以看到,编码已经变成了 hashtable。
总结本文主要介绍了 Redis 中 5 种常用数据类型中的哈希类型底层的存储结构 hashtable 的使用,以及当 hash 分布不均匀时候 Redis 是如何进行重新哈希的问题,最后了解了哈希对象的一些常用命令,并通过一些例子验证了本文的结论。