07. 算法之一致性哈希算法介绍

news2025/1/7 6:01:09

前言

哈希算法在程序开发中的很多地方都能看到他的身影,但是哈希有他的局限性,比如如果两个key哈希到同一个位置的时候,此时就不好处理。本节我们介绍一下常规处理方式。

1. 什么是哈希算法

哈希算法将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值。哈希值是一段数据唯一且极其紧凑的数值表现形式。一般用于快速查找和加密算法。

简单解释:哈希(Hash)算法,即散列函数。它是一种单向密码体制,即它是一个从明文到密文的不可逆的映射,只有加密过程 没有解密过程。同时,哈希函数可以将任意长度的输入经过变化以后得到固定长度的输出。哈希函数的这种单向特征和输出数据长度固定的特征使得它可以生成消息或者数报

2. 哈希算法在分布式场景中的应用场景

Hash算法在很多分布式集群产品中都有应⽤,⽐如分布式集群架构Redis、Hadoop、ElasticSearch,Mysql分库分表Nginx负载均衡等。

2.1 请求的负载均衡

nginx的ip_hash策略,通过对ip地址或者sessionid进⾏计算哈希值,哈希值与服务器数量进⾏取模运算,得到的值就是当前请求应该被路由到的服务器编号,如此,同⼀个客户端ip发送过来的请求就可以路由到同⼀个⽬标服务器,实现会话粘滞

2.2 分布式存储

以分布式内存数据库Redis为例,集群中有redis1,redis2,redis3 三台Redis服务器。那么,在进⾏数据存储时,<key1,value1>数据存储到哪个服务器当中呢?针对key进⾏hash处理hash(key1)%3=index, 使⽤余数index锁定存储的具体服务器节点。

分库分表的数据库可以同样用类似逻辑处理

3. 哈希算法存在的问题

3.1 哈希碰撞

index = HashCode (Key) % Array.length
int index=Math.abs("Hello".hashCode())%10;0-9

以上面的demo为例,对象Hash的前提是hashCode()方法,那么HashCode()的作用就是保证对象返回唯一hash值,但当两个对象计算值一样时,这就发生了碰撞冲突。

当我们对某个元素进行哈希运算,得到一个存储地址,然后要进行插入的时候,发现已经被其他元素占用了,其实这就是所谓的哈希冲突,也叫哈希碰撞

简单来说,就是两个不同的待存储元素,经过哈希函数计算后得到的存储位置一样,这样两个存储元素就冲突了。举个极端的例子,当长度为10 的数组要存储11个元素的时候,那么哈希碰撞一定会发生。

3.2 哈希碰撞常见解决方式

3.2.1 开放寻址法

开放寻址法的原理是当一个Key通过哈希函数获得对应的数组下标已被占用时,就寻找下一个空档位置。
在这里插入图片描述
在Java中,ThreadLocal所使用的就是开放寻址法

3.2.2 拉链法

数组的每一个元素不仅是一个Entry对象,还是一个链表的头节点。每一个Entry对象通过next指针指向它的下一个Entry节点。当新来的Entry映射到与之冲突的数组位置时,只需要插入到对应的链表中即可,默认next指向null
在这里插入图片描述
在Java中,HashMap所使用的就是链表法。(这里留意java8中,当节点元素过多的时候,为避免链表过长,链表会调整为红黑树)

3.3 分布式环境中的问题

我们知道哈希算法中很重要的两个因素,一个是哈希表,一个是哈希函数,哈希函数的好坏决定数据能不能均衡的分布在哈希表中,如果哈希算法不好的话,可能很多值会路由到同一个位置,造成频繁的哈希碰撞。极端情况会导致哈希表退化为单链表。

哈希表的长度也是很重要的因素,如果太短的话,可能会导致很多元素挂在链表上,最终退化为单链表,如果长度过长的话,会造成内存空间的浪费。

在分布式环境中,常见的场景是服务的扩容缩容,以上面提到的例子为例,比如mysql存储到瓶颈了,我们需要扩容mysql节点,此时就需要扩容。比如双十一的时候,我们为了支持更多的请求处理,在nginx后面加了多台tomcat 服务器处理请求,在双十一过后,请求没那么多,我们可以选择释放部分服务器资源,节省服务器成本。

3.4 本质

通过上面举例子可以看到,哈希算法用的好,确实可以提高我们的查询效率。但是不管是在jdk中的hashmap扩容缩容,还是我们分布式场景中的服务器资源扩容缩容,其实本质上都是对哈希表的扩容缩容。而固定的哈希函数在扩容缩容的时候,需要存储的元素重新哈希计算元素的在扩容后的哈希表中的位置。

那么,有没有其他办法,在哈希表扩容缩容的时候,不重新计算所有元素位置呢,就是下面的一致性哈希算法

4. 一致性哈希算法

⾸先有⼀条直线,直线开头和结尾分别定为为1和2的32次⽅减1,这相当于⼀个地址,对于这样⼀条线,弯过来构成⼀个圆环形成闭环,这样的⼀个圆环称为hash环。我们把服务器的ip或者主机名求hash值然后对应到hash环上,那么针对客户端⽤户,也根据它的ip进⾏hash求值,对应到环上某个位置,然后如何确定⼀个客户端路由到哪个服务器处理呢?

在这里插入图片描述
按照顺时针⽅向找最近的服务器节点
在这里插入图片描述
假如将服务器3下线,服务器3下线后,原来路由到3的客户端重新路由到服务器4,对于其他客户端没有影响只是这⼀⼩部分受影响(请求的迁移达到了最⼩,这样的算法对分布式集群来说⾮常合适的,避免了⼤量请求迁移 )
在这里插入图片描述
增加服务器5之后,原来路由到3的部分客户端路由到新增服务器5上,对于其他客户端没有影响只是这⼀⼩部分受影响(请求的迁移达到了最⼩,这样的算法对分布式集群来说⾮常合适的,避免了⼤量请求迁移 )
在这里插入图片描述

5. 手写一致性哈希算法

5.1 普通哈希

package org.wanlong.hash;

/**
 * @author wanlong
 * @version 1.0
 * @description: 普通哈希
 * @date 2023/5/23 13:37
 */
public class GeneralHash {

    public static void main(String[] args) {
        // 定义客户端IP
        String[] clients = new String[]
                {"102.178.122.12", "23.243.63.2", "8.8.8.8"};
        // 定义服务器数量
        int serverCount = 3;// (编号对应0,1,2)
        // hash(ip)%node_counts=index
        //根据index锁定应该路由到的tomcat服务器
        for (String client : clients) {
            int hash = Math.abs(client.hashCode());
            int index = hash % serverCount;
            System.out.println("客户端:" + client + " 被路由到服务器编号为:"
                    + index);
        }
    }
}

5.2 一致性哈希

package org.wanlong.hash;

import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @author wanlong
 * @version 1.0
 * @description: 一致性哈希
 * @date 2023/5/23 13:40
 */
public class ConsistHash {

    public static void main(String[] args) {
        //step1 初始化:把服务器节点IP的哈希值对应到哈希环上
        // 定义服务器ip
        String[] tomcatServers = new String[]
                {"23.23.0.0", "7.4.3.1", "7.6.6.8", "6.6.7.7"};
        SortedMap<Integer, String> hashServerMap = new TreeMap<>();
        for (String tomcatServer : tomcatServers) {
            // 求出每⼀个ip的hash值,对应到hash环上,存储hash值与ip的对应关系
            int serverHash = Math.abs(tomcatServer.hashCode());
            // 存储hash值与ip的对应关系
            hashServerMap.put(serverHash, tomcatServer);
        }


        //step2 针对客户端IP求出hash值
        // 定义客户端IP
        String[] clients = new String[]
                {"8.8.8.8","709.7.8.1","787.4.2.5"};
        for(String client : clients) {
            int clientHash = Math.abs(client.hashCode());
            //step3 针对客户端,找到能够处理当前客户端请求的服务器(哈希环上顺时针最近)
            // 根据客户端ip的哈希值去找出哪⼀个服务器节点能够处理()
            SortedMap<Integer, String> integerStringSortedMap =
                    hashServerMap.tailMap(clientHash);
            if(integerStringSortedMap.isEmpty()) {
                // 取哈希环上的顺时针第⼀台服务器
                Integer firstKey = hashServerMap.firstKey();
                System.out.println("==========>>>>客户端:" + client + " 被 路由到服务器:" + hashServerMap.get(firstKey));
            }else{
                Integer firstKey = integerStringSortedMap.firstKey();
                System.out.println("==========>>>>客户端:" + client + " 被 路由到服务器:" + hashServerMap.get(firstKey));
            }
        }

    }
}

5.3 一致性哈希包含虚拟节点

package org.wanlong.hash;

import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @author wanlong
 * @version 1.0
 * @description: 包含虚拟节点的一致性哈希
 * @date 2023/5/23 13:41
 */
public class ConsistentHashWithVirtual {
    public static void main(String[] args) {
        //step1 初始化:把服务器节点IP的哈希值对应到哈希环上
        // 定义服务器ip
        String[] tomcatServers = new String[]
                {"23.23.0.0", "7.4.3.1", "7.6.6.8", "6.6.7.7"};
        SortedMap<Integer, String> hashServerMap = new TreeMap<>();
        // 定义针对每个真实服务器虚拟出来⼏个节点
        int virtaulCount = 3;
        for (String tomcatServer : tomcatServers) {
            // 求出每⼀个ip的hash值,对应到hash环上,存储hash值与ip的对应关系
            int serverHash = Math.abs(tomcatServer.hashCode());
            // 存储hash值与ip的对应关系
            hashServerMap.put(serverHash, tomcatServer);
            // 处理虚拟节点
            for (int i = 0; i < virtaulCount; i++) {
                int virtualHash = Math.abs((tomcatServer + "#" + i).hashCode());
                hashServerMap.put(virtualHash, "----由虚拟节点" + i + "映射过 来的请求:" + tomcatServer);
            }
        }

        //step2 针对客户端IP求出hash值
        // 定义客户端IP
        String[] clients = new String[]
                {"8.8.8.8","709.7.8.1","787.4.2.5"};
        for (String client : clients) {
            int clientHash = Math.abs(client.hashCode());
            //step3 针对客户端,找到能够处理当前客户端请求的服务器(哈希环上顺时针最近)
            // 根据客户端ip的哈希值去找出哪⼀个服务器节点能够处理()
            SortedMap<Integer, String> integerStringSortedMap =
                    hashServerMap.tailMap(clientHash);
            if (integerStringSortedMap.isEmpty()) {
                // 取哈希环上的顺时针第⼀台服务器
                Integer firstKey = hashServerMap.firstKey();
                System.out.println("==========>>>>客户端:" + client + " 被 路由到服务器:" + hashServerMap.get(firstKey));
            } else {
                Integer firstKey = integerStringSortedMap.firstKey();
                System.out.println("==========>>>>客户端:" + client + " 被 路由到服务器:" + hashServerMap.get(firstKey));
            }
        }
    }
}

6. 参考文档

部分图片案例来源

以上,本人菜鸟一枚,如有错误,请不吝指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/562000.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

界面控件DevExtreme使用指南 - 如何自定义上下文菜单和工具栏

DevExtreme File Manager&#xff08;文件管理器&#xff09;小部件现在支持自定义内置的工具栏和上下文菜单&#xff0c;用户可以使用标准和定义的命令项填充项目集合&#xff0c;并配置设置来更改其外观和操作。 DevExtreme拥有高性能的HTML5 / JavaScript小部件集合&#x…

基于Keras-YOLO实现目标检测

Keras-YOLO 3项目使用Python语言实现了YOLO v3网络模型&#xff0c;并且可以导入Darknet网络预先训练好的权重文件信息直接使用网络进行目标识别。 1. 下载Keras-YOLO 3项目 执行如下命令下载Keras-YOLO 3项目代码&#xff1a; git clone https://github.com/qqwweee/keras-…

直播和短视频美颜sdk的开发流程、代码分析

目前&#xff0c;美颜技术是提高视频质量的重要手段之一&#xff0c;特别是短视频和直播两个行业。本文将介绍其开发流程和代码分析。 一、美颜SDK的开发流程 1.需求分析 首先我们需要明确的一点就是“需求”&#xff0c;例如&#xff1a;美颜效果、美颜程度、性能要求等。同…

解决找不到微信支付V3版本公钥问题

参考微信文档链接为签名验证-接口规则 | 微信支付商户平台文档中心 写的内容特别不明显&#xff0c;往下面看会找到 下载openssl工具使用命令从私钥证书中导出即可。

地图在线编辑平台,无基础轻松实现私域地图

位构云平台让用户轻松构建诸如空间信息管理、建筑信息管理及三维空间数据可视化、导航等类型应用的多平台、综合型地图引擎&#xff0c;基于OpenGLES/WebGL三维可视化技术体系的自主研发图形引擎&#xff0c;可以让开发者轻松构建运行在 Web、Android、iOS 等多平台的应用程序。…

安捷伦DSO80404B(Agilent)dso80404b租售回收 数字示波器

DSO80404B 是 Agilent 的 4 GHz、4 通道数字示波器。测量电子电路或组件中随时间变化的电压或电流信号&#xff0c;以显示振幅、频率和上升时间等。应用包括故障排除、生产测试和设计。 附加功能&#xff1a; 4 GHz 带宽&#xff0c;可升级至 13 GHz 4个模拟通道 高达 40 G…

day10 前端技术-HTMLCSS

HTML 含义:超文本标记语言,静态网页,用于在浏览器显示数据 双标签:<> </>,开始标签和结束标签同时出现 单标签: 属性名:属性后面的值都加双引号 常用的HTML标签 :文档的根标签 :HTML页面的头部标签 “”:页面标题 “”:页面主体部分 “ “ “ 到 ”:标题…

『树莓派云台机器人』02. 电脑连接树莓派 配置开发环境

目录 1. 下载ssh交互工具 Xshell 与XFTP&#xff08;有过相关使用经历的朋友可以跳过这一节内容&#xff09;2. 下载VNC远程控制工具软件3. 连接过程4. Xshell 命令工具5. XFTP 文件传送工具6. 关于联网总结 欢迎关注 『树莓派云台机器人』 博客&#xff0c;持续更新中 欢迎关注…

PoseiSwap IDO在Bounce上启动在即,如何参与?

目前&#xff0c;Nautilus Chain 生态基本完成测试&#xff0c;并即将在不久上线主网。PoseiSwap 作为 Nautilus Chain 上的首个 DEX&#xff0c;也即将面向市场并上线正式版本。我们看到&#xff0c; PoseiSwap 也正式发布了新的市场进程&#xff0c;基于其治理代币 POSE 的 I…

转辙机介绍

简介 转辙机是指用以可靠地转换道岔位置&#xff0c;改变道岔开通方向&#xff0c;锁闭道岔尖轨&#xff0c;反映道岔位置的重要的信号基础设备&#xff0c;它可以很好地保证行车安全&#xff0c;提高运输效率&#xff0c;改善行车人员的劳动强度。 分类 01、转辙机按动作时…

如何做出有价值的知识管理文档?

知识管理文档是企业中重要的资产&#xff0c;它可以帮助企业员工更好地理解业务流程、产品功能、标准操作等信息。如何做出有价值的知识管理文档&#xff0c;满足员工知识需求&#xff0c;提高工作效率&#xff0c;本文将探讨以下几个方面&#xff1a; 一、制定有效的知识管理…

jsp网上鞋城系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java 网上鞋城系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5 开发&#xff0c;数据库为Mysql&#xff0c;使用j…

建议熟知:2023谷歌新搜索规则!

谷歌作为全球最大的搜索引擎之一&#xff0c;不断更新和调整其搜索算法和规则&#xff0c;以提供更精准、高质量的搜索结果。2023年&#xff0c;谷歌搜索将迎来一系列新的搜索规则&#xff0c;同时&#xff0c;AI工具的快速发展也为谷歌搜索带来了全新的应用场景和可能性。 这…

基于Go开发PaaS平台1

Go开发PaaS平台核心功能 代码仓库地址GitHub - yunixiangfeng/gopaas 1 云原生PaaS平台介绍 随着云计算的发展&#xff0c;越来越多的企业逐步的把IT资源迁移到云上。PaaS平台作为基础设施基座&#xff0c;可以帮助企业快速构建功能丰富的容器云平台&#xff0c;提升交付效率…

应用在智能手表中监测温度的数字红外接近检测模块

智能手表是将手表内置智能化系统、搭载智能手机系统而连接于网络而实现多功能&#xff0c;能同步手机中的电话、短信、邮件、照片、音乐等。显示方式包括指针、数字、图像等。随着对于健身、运动的高度关注&#xff0c;以及大众购买力的增强&#xff0c;智能手表这个可穿戴设备…

SpringCloud 性能优化

Springcloud的性能问题 Springcloud 原始的配置&#xff0c;性能是很低的&#xff0c;大家可以使用Jmeter测试一下&#xff0c;QPS不会到50。要做到高并发&#xff0c;需要做不少的配置优化&#xff0c;主要的配置优化有以下几点&#xff1a; Feign 配置优化hystrix配置 优化…

《水经注地图服务》地图服务与通用设置说明

&#xff08;本文首发于“水经注GIS”公号&#xff0c;关注公号免费领取地图数据&#xff09; 《水经注地图服务》&#xff08;WeServer&#xff09;是一款可快速发布全国乃至全球海量卫星影像的地图发布服务产品&#xff0c;该产品完全遵循OGC相关协议标准&#xff0c;是一个基…

在 Linux 中如何使用 HAProxy、Nginx 和 Keepalived 进行负载均衡?

在现代网络应用中&#xff0c;负载均衡是提高性能和可靠性的关键因素之一。通过将请求分发到多个服务器上&#xff0c;负载均衡可以确保请求被合理地处理&#xff0c;并避免单点故障。 在 Linux 环境下&#xff0c;常用的负载均衡解决方案包括 HAProxy、Nginx 和 Keepalived。…

Vue3全家桶之数据管理

我们都知道Vue框架是通过数据驱动的&#xff0c;所以数据的重要性不言而喻&#xff0c;那么有哪些数据需要管理又是如何进行管理的呢&#xff1f;本节我们就来聊一聊Vue3中的数据管理。 哪些数据需要管理&#xff1f; 在我们的前端项目中&#xff0c;都会有哪些数据呢&#x…

Imagination推出IMG CXM最小GPU,为家庭娱乐带来无比便捷的用户界面

全新IMG CXM GPU核兼容RISC-V并原生支持全HDR&#xff0c;帮助数字电视及整个消费市场降低成本 中国北京 - 2023年5月23日 - Imagination Technologies推出全新IMG CXM GPU系列为对成本敏感的消费级设备带来无缝的视觉体验。该系列包含原生支持全HDR用户界面的最小GPU。 IMG CX…