redis的哈希算法和java的HashMap有什么差别

这个问题是一个面试官问到的

 

到现在我也没明白,他具体要问哪个? -- 最近翻邮件的时候看到有人说还是有区别的,就又去瞄了一眼

 

有查了一些资料

 

本来大概也知道旧版的HashMap基本上就是传统的数组+链表的方式实现,

 

1、对key进行hash算法,取模,比如取模20,那么数组的长度就是20

2、那么如果取模的话一定存在某些key在同一个数组索引中(也称为同一个桶中),也可以叫hash冲突,这些概念都只是为了帮助理解,没必要太纠结

那么如何解决hash冲突?就是上面说到的链表,桶中将会转换成链表结构

我们可以理解为数组是一个一级索引,链表中才是真正的表数据

 

为什么不直接用全数组的形式:空间问题

缺陷:

1、哈希冲突频繁情况下,性能问题:可能需要进行重新分配桶

2、链表数据量大的情况下,查询性能的问题

 

Java8之后增加了红黑树的实现,红黑树是自平衡的二叉树,能实现良好的查询性能,相应的就是解决链表数据量大的情况下,查询性能的问题(单桶数据量大)

 

 

那么我们再看看redis,redis中也是有hash的数据类型

 

由于redis使用的不是java语言,源码并未过多分析,这边参考下这个博客:https://blog.csdn.net/mccand1234/article/details/93411326

 

从这边来看的,这里采用的应该和旧版的HashMap实现没有太大差别。

 

所以我不太明白之前的面试官是挖坑给我,还是另有所指

 

 

所以在学习redis的同时,这边又涉及到一个切片的问题,切片这边指的是当系统承载的访问量、数据量越来越多的情况下,单机甚至普通的集群业务分层都无法满足的情况下,需要进行更细的切分

 

常用的三种切分方式:

  1、取模Hash

  2、随机(适用于消息队列模式,即不管你往哪一台机子存数据,都有消费者阻塞读取,消费掉这个数据)

  3、一致性Hash

 

这边也涉及到了hash算法,所以我也不太清楚是否面试官问的是这个

 

取模算法理论上和Java的HashMap应该也是差别不大

 

一致性Hash则有些不同

  1、构建一个0~2^32的一个环形空间(逻辑)

  2、通过对nodeName或者IP等进行hash函数取值,对应到这个环形空间的某个位置,代表具体的物理节点

  3、客户端访问的时候对数据也进行一样的hash函数取值,对应到环形空间的某个位置,注意一般这里不会直接能找到物理节点,而是通过一个顺时针或者逆时针的方式来获取,这也是这个环形空间的存在意义(帮助理解)

  

存在的问题:

  1、新增节点:会对相近的节点造成影响,导致原来指向相近节点的客户端,被重新指向新节点,造成缓存穿透;但是一般只影响一个节点,且如果只是作为缓存使用(而非数据库),应该是可以容忍的

  2、数据倾斜的问题,数据分布可能会集中到某几个节点中;这个和HashMap的桶中个别数据量特别大是一样的道理。解决方式:可以通过一些虚拟节点,来让环形中的节点更均匀

 

 

 

 

------------------------------------------------------

从网上找到的资料,简单整理了下。

暂时理解是这样(不排除理解的有问题):

 

由于redis的设计是基于简单和快速的,存储介质是内存

1、特定条件下redis采用的是压缩算法

在数据量比较少(键值对不超过512),redis的键值的长度不超过64的时候,采用的是压缩编码 ziplist和zipentry的。

在超过的时候,采用的是HashTable的结构,个人理解这个和旧版的Java HashMap实现没有太大的区别,也是通过挂链来解决冲突的问题

 

ziplist源码中的介绍

 

ziplist 是一个经过特殊编码的双向链表,旨在提高内存效率。 它存储字符串和整数值,其中整数被编码为实际整数而不是一系列字符。 它允许在 O(1) 时间内在列表的任一侧进行推送和弹出操作。 但是,由于每个操作都需要重新分配 ziplist 使用的内存,因此实际复杂性与 ziplist 使用的内存量有关。
参考文章:https://blog.csdn.net/m0_51504545/article/details/117391204

 

posted @ 2020-09-15 07:37  gabin  阅读(4075)  评论(1编辑  收藏  举报