暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

张三拿着这篇Redis Hash类型知识去和面试官对线了

码外狂徒 2021-06-21
300

Redis Hash类型详解

 Hash类型常用命令 

想学习全部的命令大家可以去Redis官网,这里面有最权威最官方的说明(https://redis.io/commands#hash),鉴于大家懒得看英文文档,那我给大家整理好(所以点个关注吧求求了QAQ)


创建一个Hash类型,直接使用HSET key field value [field value ...]就可以了,返回值类型为整数


还有一个功能一样的命令,HMSET key field value [field value ...] 返回值类型为字符串类型


取key中某个filed对应的值,用HGET key field


一次性取key中多个field对应的值,用HMGET key field [field ..]


一次性获取全部的field的值用HKEYS key


返回对应key中键值对的个数,用 HLEN key


判断当前key中是否含有某个field,可以用HEXISTS,存在则返回1,不存在则返回0


返回key中全部field和value信息,用HGETALL命令


返回key中某个field对应的value的长度,用HSTRLEN命令


返回key中全部value的信息,用HVALS命令


删除key中对应的field和value,用HDEL命令


HSETNX命令与String中的SETNX命令功能差不多,设置key中对应的field和value,如果key中不存在这个field,则成功,若已经存在则失败(注意这里判断是否存在的是field而不是key本身)


HINCRBY对value进行自增操作,这里一定要指定步长


也可以指定步长为浮点数类型,但是需要用HINCRBYFLOAT命令


好了,以上就是官网中给出的hash类型的全部命令了(除了HSCAN),相信大家已经对Hash有了一个初步了解,下面我们一起深入了解Hash的数据结构和机制吧

深入Hash类型

哈希表数据结构

Hash类型是Redis中非常重要的复合型结构,通过(K,V)来实现,采用链地址法来处理哈希冲突,整体数据结构与Java中的HashMap极为相似,都是采用数组+链表(Redis中不会将链表改为红黑树)的结构来实现,我们直接来看一下哈希表结构图

我们来一起看一下最左边dictht中的字段

   table:指向哈希表的指针

   size:哈希表数组的大小

   sizemask:用于计算哈希值的掩码

   used:已经存储的(K,V)个数



哈希表数据结构

Hash类型是Redis中非常重要的复合型结构,通过(K,V)来实现,采用链地址法来处理哈希冲突,整体结构与Java中的HashMap极为相似,都是采用数组+链表(Redis中不会将链表改为红黑树)的结构来实现,我们直接来看一下哈希表结构图

我们来一起看一下最左边dictht中的字段

    table:指向哈希表的指针

    size:哈希表数组的大小

    sizemask:用于计算哈希值的掩码

    used:已经存储的(K,V)个数



Hash类型完整结构

Hash类型的完整结构如下

字典类型的中其实包含两个哈希表,一个是我们刚刚讲到用于存储(K,V)的哈希表ht[0],而另外一个ht[1]用于后续的扩容



Rehash机制

字典类型容量变化过程叫做rehash,需要满足一定的条件才能触发扩容机制

服务器当前没有进行BGWRITEAOF或者BGSAVE命令,且当前键值对个数超过一维数组的大小(这两个命令是用于Redis持久化的,挖个坑,改天给你们发一篇Redis持久化的),才会触发扩容。


如果当前键值对个数超过一维数组大小的五倍,无论是否在进行BGWRITEAOF或者BGSAVE命令,都会强制扩容

Hash类型扩容后数组的长度为原来的二倍



缩容机制:如果当前键值对个数少于一维数组大小的十分之一,则触发缩容过程。缩容不会考虑当前服务器是否在进行BGWRITEAOF或者BGSAVE命令



渐进式Rehash

当触发扩容的时候,Redis会首先为ht[1] 分配一块内存空间。如果当前字典是一个比较大的字典,那么整个扩容过程的时间复杂度为O(n),直接完整进行扩容机制可能会导致Redis一段时间内停止服务。为了避免停止服务的情况,Redis的设计团队采用了渐进式rehash的策略,每次只对原哈希表中的一小部分进行搬迁,这样渐进式的进行,直到全部键值对都迁移到新的哈希表中。


看到这里你可能会问,如果搬迁的过程中我们需要查询键值会怎么办?别急,这个问题我们一步一步来看

首先,对于key的查询,我们需要到原来的哈希表中进行查找,如果找到对应的value,直接返回就可以了。如果没有找到,那么只有两种可能,一个是这个键值对已经搬迁到新的哈希表了,另外一种可能是根本就不存在这个键值对,无论是哪种可能,我们都需要再去新哈希表中对他进行查找,如果找到了就返回,如果找不到说明这个键值对不存在。

用上面的图片来举个例子,假设原来(k1,v1),(k2,v2),(k3,v3)都挂在数组下标为0的位置,在扩容时,我们采用渐进式搬迁的策略,此时(k1,v1),(k2,v2)已经完成搬迁。如果此时客户端发出了查询k4的请求,我们会先在oldHashTable中进行查询,如果查询到则返回,如果查询不到就去newHashTable中查询,显然我们无法在oldHashTable中查询到k4对应的值,所以我们再去newHashTable中进行查询,如果这次也没有查到说明不存在k4以及对应的值


可能还有人会问,如果现在要添加一个键值对呢?添加KV的操作可以直接到新哈希表中进行添加,不用考虑原来旧的哈希表


字典类型的另外一种实现

其实字典类型除了上面说的哈希表结构,还存在一种ziplist(压缩列表)的结构,不过这种结构只适用于键值对较少的情况下。

可以看到,当前test中只包含一个键值对,此时整个字典使用压缩列表来实现的。压缩列表会在内存中申请一块连续的内存空间

在这种情况下查找元素的时间复杂度为O(n),并不适合存储大量键值对。使用ziplist作为哈希表的条件如下:

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

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

如果不满足上述两个条件,则自动转换为hashtable类型

总结

Hash类型是Redis中一个非常重要的数据结构,在平时开发中我们可以将对象转为Hash类型进行存储。其实整个Redis就是一个大的Hash类型,所有的数据结构都是通过K-V的结构来进行定位。

好了,终于又水了一期,上一篇String类型的阅读量有点低,不知道大家想看什么内容呢。大家有想看的都可以后台给我留言,我会尽力给大家做的,后续Redis还会出三篇基础数据结构、持久化、高可用、内存回收和一些常见的面试题,Redis写完就开始更MySQL,希望大家可以多多关注。


参考链接

https://mp.weixin.qq.com/s/vXBFscXqDcXS_VaIERplMQ

https://blog.csdn.net/codejas/article/details/88582831

《Redis深度历险》


微信号|码外狂徒

文章转载自码外狂徒,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论