哈希冲突和一致性哈希

2023-05-16

文章目录

    • 哈希冲突
      • 处理哈希冲突
        • 1.开放地址法
        • 2.再散列法
        • 3.链地址法
        • 4 建立一个公共溢出区
    • 一致性哈希
      • 普通 hash算法
        • 普通 hash 算法的缺陷
      • 一致性哈希算法
        • 一致性 hash 算法的优点
        • hash 环的倾斜与虚拟节点

哈希冲突

哈希函数又称“散列函数”,通过某种算法,可以将任意长度的输入转换为固定长度的输出。
如在Java中,哈希码用整型int表示,这意味着一共只有2的32次方个哈希码,而对象的个数可以理解为无限个,哈希函数可以将无限个对象实例转换成有限个的哈希码。Object::hashCode默认就是通过对象所在的内存地址经过处理计算而得出的。

哈希函数的目的是将任意长度的输入转换为固定长度的输出,这就意味着,不同的输入可能会转换成相同的输出,这就导致了「哈希冲突」,也叫「哈希碰撞」。哈希冲突是客观事实,只能尽量避免,无法彻底解决。

处理哈希冲突

一个设计良好的哈希函数,应该让不同特征的对象具有不同的哈希码,尽可能的避免哈希冲突。
但是哈希冲突是客观事实,只能尽量避免,无法彻底解决。因此,一旦发生了哈希冲突,如何解决就变得非常重要了。

哈希冲突常见的解决方式有如下四种方法:

1.开放地址法

开放地址法,有三种形式,分别是线性探测,二次探测和随机探测法

线性探测
如下图,假设哈希码为0-5,哈希表中已经插入8、9元素,此时再插入14,下标2已经被8给占用了,出现哈希冲突。
线性探测会环形寻找next节点,先找到下标3,被9占用了,依然冲突,再找到下标4,没有被占用,即没有发生冲突,则将14放入下标4的节点中。
在这里插入图片描述

二次探测
也称二元探测,如果默认的哈希函数计算出的哈希码发生了哈希冲突,则哈希函数升级为:

(hash(key) + d) % table.length;
d = 1^2, -1^2, 2^2, -2^2, 3^2......1,-1,2,-2,4,-4,9...)
其中,table.length为哈希表的表长;d 是产生冲突的时候的增量序列。

随机探测
和二次探测类似,只是d会更换为一组伪随机数列。

(hash(key) + d) % table.length;
d = 一组伪随机数列

开放定址法的优点就是:只要哈希表还有位置,通过不断的探测,总能找到合适的位置。
缺点是探测的次数不可控,一旦探测次数骤增,会严重影响哈希表的读写性能。

ThreadLocalMap就是用的「线性探测」技术解决哈希冲突的,当线程的ThreadLocal实例数量较多时,ThreadLocal的读效率会下降,因此Netty、Dubbo才会编写自己的ThreadLocal实现。

2.再散列法

也可以称作再哈希法,提供一组哈希函数,而不是一个。
如果第一个哈希函数计算的哈希码发生冲突了,就采用第二个哈希函数重新计算哈希码,直到不冲突为止。、
查询时也是一样,依次调用不同的哈希函数计算哈希码,直到Key相等。
这种方式会增加哈希计算的开销,影响读写的效率。

int hash = hash1(key)hash2(key)hash3(key)......

3.链地址法

也称作拉链法,将哈希码对应一个链表,插入元素时,如果哈希码冲突了,就将元素插入到链表,可选头插或尾插。
查询时,遍历哈希码对应的链表。
HashMap采用的就是这种方式。

这种方式的缺点是:一旦哈希冲突多了,哈希表会退化成链表,查询效率会从O(1)变为O(n)。JDK8的HashMap针对这种情况有做优化,冲突超过8个会将链表转换为红黑树,提高查询效率。

4 建立一个公共溢出区

在创建哈希表的同时,再额外创建一个公共溢出区,专门用来存放发生哈希冲突的元素。查找时,先从哈希表查,查不到再去公共溢出区查。
这种方式的缺点是:哈希冲突多了,公共溢出区会膨胀的非常厉害,查询的效率也有影响。

一致性哈希

普通 hash算法

对于一个经典的分布式缓存的应用场景。假设我们有三台缓存服务器,用于缓存图片,我们为这三台缓存服务器编号为 0号、1号、2号。我们希望将3万张图片被均匀的缓存到这3台服务器上,以便它们能够分摊缓存的压力。
常见的做法是对缓存项的键进行哈希,将hash后的结果对缓存服务器的数量进行取模操作,通过取模后的结果,决定缓存项将会缓存在哪一台服务器上。

hash(图片名称)% N

当我们对同一个图片名称做相同的哈希计算时,得出的结果应该是不变的,如果我们有3台服务器,使用哈希后的结果对3求余,那么余数一定是0、1或者2;如果求余的结果为0, 就把当前图片缓存在0号服务器上,如果余数为1,就缓存在1号服务器上。
以此类推;同理,当我们访问任意图片时,只要再次对图片名称进行上述运算,即可得出图片应该存放在哪一台缓存服务器上,我们只要在这一台服务器上查找图片即可,如果图片在对应的服务器上不存在,则证明对应的图片没有被缓存,也不用再去遍历其他缓存服务器了,通过这样的方法,即可将3万张图片随机的分布到3台缓存服务器上了,而且下次访问某张图片时,直接能够判断出该图片应该存在于哪台缓存服务器上,我们暂时称上述算法为普通 HASH 算法或者取模算法

普通 hash 算法的缺陷

上述HASH算法时,会出现一些缺陷:如果服务器已经不能满足缓存需求,就需要增加服务器数量,假设我们增加了一台缓存服务器,此时如果仍然使用上述方法对同一张图片进行缓存,那么这张图片所在的服务器编号必定与原来3台服务器时所在的服务器编号不同,因为除数由3变为了4,最终导致所有缓存的位置都要发生改变,也就是说,当服务器数量发生改变时,所有缓存在一定时间内是失效的,当应用无法从缓存中获取数据时,则会向后端服务器请求数据;同理,假设突然有一台缓存服务器出现了故障,那么我们则需要将故障机器移除,那么缓存服务器数量从3台变为2台,同样会导致大量缓存在同一时间失效,造成了缓存的雪崩**,后端服务器将会承受巨大的压力,整个系统很有可能被压垮**。为了解决这种情况,就有了一致性哈希算法

一致性哈希算法

一致性哈希算法也是使用取模的方法,但是普通取模算法是对服务器的数量进行取模,而一致性哈希算法是对 2^32 取模,具体步骤如下:

  • 步骤一:一致性哈希算法将整个哈希值空间( 2^32 )按照顺时针方向组织成一个虚拟的圆环,称为 Hash 环;
  • 步骤二:接着将各个服务器使用 Hash 函数对 2^32 取模进行哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,从而确定每台机器在哈希环上的位置
  • 步骤三:最后使用算法定位数据访问到相应服务器:将数据key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针寻找,第一台遇到的服务器就是其应该定位到的服务器

下面我们使用具体案例说明一下一致性哈希算法的具体流程:

(1)步骤一:哈希环的组织:

我们将 2^32 想象成一个圆,像钟表一样,钟表的圆可以理解成由60个点组成的圆,而此处我们把这个圆想象成由2^32个点组成的圆,示意图如下:
在这里插入图片描述
圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,2、3、4、5、6……直到2^32-1 ,也就是说0点左侧的第一个点代表2^32-1,我们把这个由 2^32 个点组成的圆环称为hash环。

(2)步骤二:确定服务器在哈希环的位置:

哈希算法:hash(服务器的IP) % 2^32

上述公式的计算结果一定是 0 到 2^32-1 之间的整数,那么上图中的 hash 环上必定有一个点与这个整数对应,所以我们可以使用这个整数代表服务器,也就是服务器就可以映射到这个环上,假设我们有 ABC 三台服务器,那么它们在哈希环上的示意图如下:
在这里插入图片描述
(3)步骤三:将数据映射到哈希环上:

我们还是使用图片的名称作为 key,所以我们使用下面算法将图片映射在哈希环上:hash(图片名称) % 2^32,假设我们有4张图片,映射后的示意图如下,其中橘黄色的点表示图片:
在这里插入图片描述
那么,怎么算出上图中的图片应该被缓存到哪一台服务上面呢?我们只要从图片的位置开始,沿顺时针方向遇到的第一个服务器就是图片存放的服务器了。最终,1号、2号图片将会被缓存到服务器A上,3号图片将会被缓存到服务器B上,4号图片将会被缓存到服务器C上。

一致性 hash 算法的优点

前面提到,如果简单对服务器数量进行取模,那么当服务器数量发生变化时,会产生缓存的雪崩,从而很有可能导致系统崩溃,而使用一致性哈希算法就可以很好的解决这个问题,因为一致性Hash算法对于节点的增减都只需重定位环空间中的一小部分数据,只有部分缓存会失效,不至于将所有压力都在同一时间集中到后端服务器上,具有较好的容错性和可扩展性

假设服务器B出现了故障,需要将服务器B移除,那么移除前后的示意图如下图所示:
在这里插入图片描述
在服务器B未移除时,图片3应该被缓存到服务器B中,可是当服务器B移除以后,按照之前描述的一致性哈希算法的规则,图片3应该被缓存到服务器C中,因为从图片3的位置出发,沿顺时针方向遇到的第一个缓存服务器节点就是服务器C,也就是说,如果服务器B出现故障被移除时,图片3的缓存位置会发生改变,但是,图片4仍然会被缓存到服务器C中,图片1与图片2仍然会被缓存到服务器A中,这与服务器B移除之前并没有任何区别,这就是一致性哈希算法的优点。

hash 环的倾斜与虚拟节点

一致性哈希算法在服务节点太少的情况下,容易因为节点分部不均匀而造成数据倾斜问题,也就是被缓存的对象大部分集中缓存在某一台服务器上,从而出现数据分布不均匀的情况,这种情况就称为 hash 环的倾斜。如下图所示:
在这里插入图片描述

hash 环的倾斜在极端情况下,仍然有可能引起系统的崩溃,为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点,一个实际物理节点可以对应多个虚拟节点,虚拟节点越多,hash环上的节点就越多,缓存被均匀分布的概率就越大,hash环倾斜所带来的影响就越小,同时数据定位算法不变,只是多了一步虚拟节点到实际节点的映射。具体做法可以在服务器ip或主机名的后面增加编号来实现,加入虚拟节点以后的hash环如下:
在这里插入图片描述
参考文章:
哈希冲突的常见解决方式
一致性哈希算法原理详解

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

哈希冲突和一致性哈希 的相关文章

  • 阶段进度条

    public class PhaseProgressView extends View 节点连线宽度 private int mLineWidth 节点个数 private int mNodeNum 选中节点位置 private int m
  • 计算机网络实验——基于TCP协议的socket编程

    一 实验目的 1 实现一个能够在局域网中进行点对点聊天的实用程序 2 熟悉c 43 43 Java等高级编程语言网络编程的基本操作 3 基本了解对话框应用程序的编写过程 4 实现TCP套接字编程 二 实验内容 xff08 一 xff09 实
  • GPRMC 格式

    数据示例 GPRMC 081057 000 A 3117 2144 N 12133 2691 E 0 02 0 00 140521 D 68 在数据 GPRMC lt 1 gt lt 2 gt lt 3 gt lt 4 gt lt 5 gt
  • 音视频开发基础概念

    对一个初学者来说 xff0c 刚刚接触音视频的学习难免会遇到各种个样的术语 xff0c 一开始我也是云里雾里的 xff0c 到现在一点一点接触积累 xff0c 形成一个基本的认识 本文并没有什么高深和详细的知识点 xff0c 旨在记录一些音
  • 音频数据采集-AudioRecord

    一 AudioRecord 和 MediaRecorder Android 提供了两个 API 用于录音 xff0c AudioRecord 和 MediaRecorder AudioRecord 能够获取原始的 PCM 数据 xff0c
  • vector用法

    介绍 1 vector是表示可变大小数组的序列容器 2 就像数组一样 xff0c vector也采用的连续存储空间来存储元素 也就是意味着可以采用下标对vector的元素进行访问 xff0c 和数组一样高效 但是又不像数组 xff0c 它的
  • AAC 音频编码保存和解码播放

    一 编码器 MediaCodec MediaCodec 是 Android 提供的用于对音频进行编解码的类 xff0c 属于硬编解 MediaCodec 在编解码的过程中使用了一组缓冲区来处理数据 如下图所示 基本使用流程如下 xff1a
  • Camera 视频采集,H264 编码保存

    一 前言 上篇文章 AAC 音频编码保存和解码播放 讲述了通过 AudioRecord 录制音频数据 xff0c 并通过 AAC 编码保存为 AAC 文件 这里的 aac 既是一种编码方式 xff0c 也是一种容器 xff0c 因此可以直接
  • 基于Camera、AudioRecord 、MediaCodec 和 MediaMuxer 录制 MP4

    一 前言 在 AAC 音频编码保存和解码播放和Camera 视频采集 xff0c H264 编码保存 两篇文章中介绍了如何通过 AudioRecord 和 MediaCodec 录制 AAC 音频以及如何通过 Camera 和 MediaC
  • 基于 SurfaceView、AudioTrack、MediaCodec 和 MediaExtractor 解码 MP4 播放

    一 前言 上篇文章介绍了 基于Camera AudioRecord MediaCodec 和 MediaMuxer 录制 MP4 录制的过程是这样的 xff0c 那么相应的播放过程就是上述过程的逆过程 xff0c 本篇文章将介绍如何通过 M
  • 研发、运营必备实用工具网站

    目录 1 搜索引擎 2 PPT 3 图片操作 4 文件共享 5 招聘求职 6 程序员面试题库 7 办公 开发软件 8 高清图片 视频素材网站 9 项目开源 10 算法 11 在线工具宝典大全 12 音乐 13 神辅助工具 14 语音处理 1
  • 让人“眼前一亮、不明觉厉”的互联网技术PPT

    目录 1 互联网 1 1 智能 43 1 2 云计算 1 3 5G 2 大数据 2 1 用户画像 2 2 边缘计算 2 3 工业大数据 2 4 医疗大数据 2 5 数据平台 2 6 银行大数据 3 物联网 3 1 物联网产业 3 2 工业物
  • 基于FPGA的电梯控制系统设计

    在本项目中一共分为了五个模块 xff1a 时钟分频 按键消抖 状态控制 蜂鸣 译码显示及流水指示灯 其模块的作用分别是 xff1a 时钟分频 xff1a 将高频率系统时钟通过分频得到不同合适频率的时钟频率作为不同模块的输入时钟 clk xf
  • 【流媒体视频监控平台开发wvp-GB28181-pro】

    wvp GB28181 pro学习心得 wvp与GB28181介绍1 流媒体服务器视频协议介绍2 市面上的流媒体服务器3 wvp GB28181 pro框架需要学习的框架和工具4 工具准备项目整合和配置 wvp与GB28181介绍 学习原因
  • gazebo创建机器人模型06

    gazebo创建机器人模型05 kinect 信息仿真以及显示 kinect摄像头仿真基本流程 xff1a 1 已经创建完毕的机器人模型 xff0c 编写一个单独的 xacro 文件 xff0c 为机器人模型添加 kinect 摄像头配置
  • 【C++】C++中防止头文件重复包含的两种方法

    文章目录 01 错误分析 xff1a 类型重定义 xff08 头文件重复包含 xff09 02 解决方案2 1 微软宏2 2 条件编译2 3 两种方法比较 03 变量被重复包含3 1 解决办法 04 版权声明 amp 总结 01 错误分析
  • C/C++程序员应聘常见面试题深入剖析

    1 引言 本文的写作目的并不在于提供C C 43 43 程序员求职面试指导 xff0c 而旨在从技术上分析面试题的内涵 文中的大多数面试题来自各大论坛 xff0c 部分试题解答也参考了网友的意见 许多面试题看似简单 xff0c 却需要深厚的
  • Nvidia jetson tx2 详细安装、配置教程以及固定ip

    jetson tx2 是什么 一 硬件组装 xff1a 1 将 Wi Fi 天线插上 xff0c 组装好充电器即可 2 接口介绍 xff1a USB接口只有一个 xff08 建议使用USB拓展 xff0c 方便前期配置的时候连接键盘鼠标 x
  • C语言 用指针实现字符串函数 strlen strcpy strcat strcmp

    目录 一 指针模拟实现strlen 函数 1 strlen 函数description 2 用指针实现且将strlen封装 3 运行结果 二 指针模拟实现strcpy 函数 1 strcpy 函数description 2 用指针实现且将s
  • 常用器件介绍

    常用器件介绍 这篇文章主要介绍一些在做电子设计时最最常见的器件 xff0c 针对的是完全的小白们 面包板 首先是搭建设计电路用的面包板 面包板正面图 背面拆解图 面包板是专为电子电路的无焊接实验设计制造的 xff0c 上面有很多小插孔 各种

随机推荐

  • VScode代码格式化解决方案c/c++

    前贴链接 xff1a https tieba baidu com p 7891213649 之前说过研究出来了会和大家分享一下自己是如何解决的 xff0c 于是就有了此贴 首先要说明 xff0c 本文主要是针对c c 43 43 xff0c
  • C语言输入和输出

    文章目录 一 数据输入二 数据输出三 断章取义四 printf输出1 输出描述性的文字2 输出整数3 输出字符4 输出浮点数5 输出字符串6 输出多个内容7 示例 xff08 book12 c xff09 五 scanf输入1 输入整数2
  • c 和 python语法 对比

    直接干货 xff1a 先稍微了解两种语言的基本不同 pythonc执行方式无需手动编译 xff0c 执行时按行读取 xff0c 自动编译成字节码 所以python可轻易被获取源码需要手动进行编译生成机器可直接执行的机器码内存管理有自己的垃圾
  • 删除typora图片目录下失效的图片脚本

    背景 xff1a md中插入图片与word不同 xff0c 在word中图片是拷贝了一份放在文件中 xff0c 与原图片无关 xff0c 所以无论删除哪个互不影响 xff0c 但是md中却不同 xff0c md中插入图片时用超链接的方式从外
  • scanf中‘\n‘的用法和隐患

    读到这篇文章的人90 的可能是遇到了输入字符后 xff0c 回车没有预期的输出 xff0c 连续回车都没用 这种情况可能是因为你在scanf中加了 n xff0c 而且加了不该加的地方 把代码里的 n删了或者再输入一个不是 n的字符再回车
  • c语言使用一维数组实现杨辉三角

    通常使用在实现杨辉三角时使用的时使用二维数组的方式 xff0c 这种方式比较快捷 xff0c 且比较好理解 xff0c 但是使用二维数组浪费了大量的空间 xff0c 又大概一般的空间未被使用 如果使用一维数组进行计算能大大提高空间利用率 首
  • 链表、结构体和数组对比

    结构体和数组 1 结构体可以存不同类型的元素 而数组只能存同一类型 2 结构体类型需要我们自已定义 数组是用别的类型加 元素个数 3 结构体内存分配方式很特别 使用对齐原则 不一定是所有元素的字节数和 而数组一定是所有元素的字节数和 4 结
  • C和C++那些事儿

    1 new delete malloc free关系 delete会调用对象的析构函数 和new对应free只会释放内存 xff0c new调用构造函数 malloc与free是C 43 43 C语言的标准库函数 xff0c new del
  • 排序和复杂度

    常见的排序方式 1 冒泡排序 xff1a 时间复杂度 xff1a 最好情况是 O n xff0c 最坏情况是 O n2 空间复杂度 xff1a 开辟一个空间交换顺序O 1 2 快速排序 xff1a 时间复杂的 xff1a 最好情况是 O n
  • 文件IO过程

    基本概念 xff1a 文件描述符 xff1a xff08 文件描述符有时称为文件句柄 xff09 进程级 xff09 文件描述符是一个简单的整数 xff0c 用以标明每一个 被 进程所打开的文件和socket 通常0 1 2被标准输入 标准
  • private与构造函数

    通常我们都将构造函数的声明置于public区段 xff0c 假如我们将其放入private区段中会发生什么样的后果 xff1f 没错 xff0c 我也知道这将会使构造函数成为私有的 xff0c 这意味着什么 xff1f 我们知道 xff0c
  • 使用VS Code连接远程服务器

    目录 一 VS Code的安装与下载 二 安装插件 三 添加服务器连接配置 四 连接服务器 一 VS Code的安装与下载 关于VS Code的安装与下载及VS Code的使用方式详见如下链接 VSCode安装教程并配置C C 43 43
  • C语言 转换10进制为16进制

    实际上就是除16取余然后将其本身除以16 xff0c 得到的这一个数将它转换为具体的16进制数字的过程 xff0c 当然最后还要注意前面的字符位置的添加 span class token comment 进制之间互相转换 xff1a 将十进
  • TCP协议是如何保证传输可靠性的

    TCP确保传输可靠性的方式 校验和序列号 确认应答超时重传连接管理流量控制 xff08 滑动窗口控制 xff09 拥塞控制 校验和 xff1a TCP校验和是一个端到端的校验和 xff0c 由发送端计算 xff0c 然后由接收端验证 其目的
  • TCP的三次握手与四次挥手详解

    文章目录 TCP 协议简述TCP包首部TCP 三次握手建立连接TCP 四次挥手关闭连接常见面试题 xff1a TCP 协议简述 TCP 提供面向有连接的通信传输 xff0c 面向有连接是指在传送数据之前必须先建立连接 xff0c 数据传送完
  • IP数据报格式及分片与重组

    IP数据报 在 TCP IP 协议中 xff0c 使用 IP 协议传输数据的包被称为 IP 数据报 xff08 也叫数据包或数据报文 xff09 xff0c 每个数据包都包含 IP 协议规定的内容 IP协议提供不可靠无连接的数据报传输服务
  • mysql锁机制

    MySQL的锁机制 文章目录 MySQL的锁机制1 行锁2 表锁3 页锁4 乐观锁和悲观锁4 1悲观锁4 2乐观锁5 1InnoDB锁的特性 首先对mysql锁进行划分 xff1a 按照锁的粒度划分 xff1a 行锁 表锁 页锁按照锁的使用
  • uboot开发流程

    uboot其实就是一段比较复杂的单片机代码用来作为引导程序 xff0c 它的主要任务是初始化硬件设备 xff0c 将系统的软硬件环境带到一个合适的状态 xff0c 再将内核从一种存储介质读入到内存中 xff0c 然后跳到内核的入口点去运行
  • java的几种IO

    Java IO方式大体上可以分为三类 xff0c 基于不同的io模型可以简单分为同步阻塞的BIO 同步非阻塞的NIO和异步非阻塞的AIO IO又主要可以分为文件IO和网络IO 针对Java的网络IO模型 xff0c 可以看网络IO模型 xf
  • 哈希冲突和一致性哈希

    文章目录 哈希冲突处理哈希冲突1 开放地址法2 再散列法3 链地址法4 建立一个公共溢出区 一致性哈希普通 hash算法普通 hash 算法的缺陷 一致性哈希算法一致性 hash 算法的优点hash 环的倾斜与虚拟节点 哈希冲突 哈希函数又