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