当前位置: 首页 > news >正文

一致性哈希算法原理

文章目录

  • 前言
  • 正文
    • 一、抛砖引玉,简单Hash算法的短板
    • 二、一致性Hash算法的基本概念
      • 2.1 一致性Hash算法本质也是取模
      • 2.2 便于理解的抽象,哈希环
      • 2.3 服务器如何映射到哈希环上
      • 2.4 对象如何映射到哈希环上,并关联到对应的机器
    • 三、一致性Hash算法在增加或减少机器时的骚操作
      • 3.1 服务器数量减少
      • 3.2 服务器数量增加
    • 四、数据倾斜、机器负载的处理
      • 4.1 数据倾斜的体现
      • 4.2 机器负载的体现
      • 4.3 处理方案,增加虚拟节点
    • 五、Java代码模拟算法
      • 5.1 定义哈希算法接口
      • 5.2 简单哈希算法
      • 5.3 一致性哈希算法(不包含虚拟节点)
      • 5.4 一致性哈希算法(包含虚拟节点)
      • 5.5 测试
      • 5.6 测试结果
    • 六、一致性哈希算法的应用场景

前言

首先我们学习和了解一个知识时,可能会先下意识搜索一下它的基本概念。所以我先百度了一下。

百度给出的概念,可以说是很明确了:

一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。
在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。
一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题 。

正文

根据百度,我们可以知道,所谓一致性Hash算法,是一种特殊的Hash算法。

那么学习的捷径就出来了,我们可以对比学习,先来看看Hash算法,然后再去研究它对于解决动态伸缩问题上的短板究竟是什么。

一、抛砖引玉,简单Hash算法的短板

我们先假设出这样一个场景:

当前有3台机器 node1node2node3,是我们用来存储缓存的。业务数据缓存目前假设有100万个key。
我们的需求是,将这100万个key 尽可能均匀的存储到这3台机器上。

效果图如下:
在这里插入图片描述
当我们采取简单Hash算法时,对所有的key值进行hash计算,获取到一个hash值,再取模于3(3台机器,所以取模于3)。

这样就能保证所有算出来的结果一定是 0、1、2 这3个数,从而对应到我们的3台机器,就可以解决以上的问题。如下图所示:
在这里插入图片描述

那么问题来了,在分布式场景中,我们的机器会有数量调整的情况。
就好比电商相关的项目,在618、双11等购物狂欢节时,就需要临时增加机器,然后等高峰期过后,又需要减少节点。

这种时候,对机器数量取模,就不好使了。

假设原先有3台机器,存值时使用的是取模于3,在用的时候,有一台挂了,取值时取模于2,如下图:
在这里插入图片描述
这种时候,由于node3挂掉,取模模于2,自然会有很多缓存无法命中,也就是失效。

那么大量缓存同时失效,就可以恭喜你了,喜提缓存雪崩,整个缓存系统不可用,这也就是简单Hash算法无法处理的短板,也正是一致性Hash算法所解决的问题之一。

二、一致性Hash算法的基本概念

它的基本概念其实就和我百度出来的相同。可以翻到本文前言,再重新读一遍,理解一下。

本小节内容主要是基于百度出来的基本概念,进行图文讲解。

2.1 一致性Hash算法本质也是取模

一致性Hash算法,本质上也是取模运算,只是与简单Hash算法不同,它是对 2 的 32 次方 取模。
原因如下:

我们都知道IPv4地址,是由 4 段 8位二进制组成的,因为书写方便的问题,将二进制转换为十进制,例如如下地址:
在这里插入图片描述
而这个地址的大小范围则是 [0, 2的32次方),所以取模的值可以固定下来,能够保证每个IP地址有唯一的映射。

2.2 便于理解的抽象,哈希环

上一小节提到,IP地址的范围是 [0, 2的32次方),如果我们将所有的值抽象成环,正上方为0,以顺时针排列。
将所有的值抽象为圆周上的点,那么可以获得如下的哈希环:
在这里插入图片描述

2.3 服务器如何映射到哈希环上

假设现在有3台机器,分别是 nodeA 、nodeB 、nodeC,我们要把他们映射到哈希环中。

首先使用哈希算法对机器的IP地址进行计算,随后再取模,得到 A、B、C 这3个值。随后根据计算结果,映射到哈希环上。
在这里插入图片描述

2.4 对象如何映射到哈希环上,并关联到对应的机器

假设有4个对象,需要存起来。

对象计算时的hash函数可以和服务器节点的函数不同,但需要保证计算结果在环的范围内。
计算的结果分别是 H1,H2,H3,H4,将他们先映射到Hash环上。

在这里插入图片描述
然后从0开始,顺时针转圈,当对象计算出的结果蓝色H,第一次遇到绿色(机器),就存到该机器上。关联后的结果如下:
在这里插入图片描述

H2和H4存到了 B 机器上;H1存到了C机器上,H3存到了A机器上

本质是将原本单个点的Hash映射,转变成了环上的某个片段的映射,也就是百度百科中提到的:

在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。

这一原理的实现,也就在于此。我们接着往下看。

三、一致性Hash算法在增加或减少机器时的骚操作

3.1 服务器数量减少

比如C机器挂了,H1会存到A机器上

在这里插入图片描述

3.2 服务器数量增加

比如增加机器D,H2会存到D机器上

在这里插入图片描述

四、数据倾斜、机器负载的处理

但凡使用了Hash算法,就可能存在数据倾斜的问题(这里我们不考虑哈希冲突)。上述例子中,为了方便演示,基本使用了比较均衡的散列示例。

这里我们需要考虑两个问题:

  • 数据倾斜,也就是Hash散列不均匀,可能给一个机器存储过多的对象数据,而有的机器却一个都不存储;
  • 机器负载不均衡,在增加机器时,不能很好的分摊机器负载或只分担很少机器的负载,在现象上看,其实勉强也算是一种数据倾斜的问题;

4.1 数据倾斜的体现

很多对象集中映射到某一台或几台机器,其他机器数据映射很少甚至于没有。
可以看到很多数据都映射到了B机器,而A 、C 机器上映射的数据很少。
在这里插入图片描述

4.2 机器负载的体现

在数据倾斜发生时,本身就是机器负载不均衡的一种体现,但本小节提到的,是另一种场景。

在我们增加机器时,新增的机器不能很好的分担其他机器的负载,这也是一个问题。

比如,为了帮忙分担负载,增加了机器D:
在这里插入图片描述

但是D映射的位置比较尴尬,没有帮助B 分担到负载。

又或者说另一种情况,A 机器现在也有很多数据存储,我们新增机器D:
在这里插入图片描述
D的出现,帮B分担了部分压力,但是对A没有任何影响。

4.3 处理方案,增加虚拟节点

为了处理以上的两个问题,可以引入虚拟节点的概念。
所谓虚拟节点,就是针对每个实际机器,计算多个映射点。

比如:
针对nodeA 计算的虚拟节点有 A#1 、A#2 、A#3 ,实际节点是 A
针对nodeB 计算的虚拟节点有 B#1 、B#2 、B#3,实际节点是 B
针对nodeC 计算的虚拟节点有 C#1 、C#2 、C#3,实际节点是 C

他们分布在哈希环上之后,就如下图所示:
在这里插入图片描述
使用虚拟节点时,可以看到当虚拟节点越多,也就越不容易出现数据倾斜。

然后我们再依据虚拟节点对真实节点做一套转换即可。
也就是原先找节点的逻辑基本不变,只是在找到虚拟节点时,一律定位到实际机器上。

比如,我们找到数据H3 在 A#2虚拟节点上,就在此定位到A机器真实的节点A上。

五、Java代码模拟算法

Java 版本使用Java 9
涉及到的类文件有:
在这里插入图片描述

5.1 定义哈希算法接口

定义接口,用以实现3种哈希算法

package com.example.demo;import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;/*** 哈希算法接口** @author feng*/
public interface HashAlgorithm {/*** 哈希算法** @param clients 客户端集合* @return 服务端地址和客户端地址列表的映射*/SortedMap<String, List<String>> hash(Set<String> clients);/*** 服务器的编号、地址*/SortedMap<Integer, String> SERVER_ADDRESS_MAP = new TreeMap<>();
}

5.2 简单哈希算法

package com.example.demo;import java.util.*;/*** 简单Hash算法** @author feng*/
public class GeneralHashAlgorithm implements HashAlgorithm {private final int serverCount;public GeneralHashAlgorithm(int serverCount) {this.serverCount = serverCount;}@Overridepublic SortedMap<String, List<String>> hash(Set<String> clients) {SortedMap<String, List<String>> resultMap = new TreeMap<>();for (String client : clients) {// 执行hash,获取值int hashCode = Math.abs(client.hashCode());// 计算索引int index = hashCode % serverCount;// 获取选中的服务器地址String serverAddress = SERVER_ADDRESS_MAP.get(index);System.out.printf("客户端%s 选中了服务端序号为%s,地址为%s \r\n", client, index, serverAddress);List<String> newArrayList = resultMap.getOrDefault(serverAddress, new ArrayList<>());newArrayList.add(client);resultMap.put(serverAddress, newArrayList);}return resultMap;}
}

5.3 一致性哈希算法(不包含虚拟节点)

package com.example.demo;import java.util.*;/*** 一致性哈希算法-不包括虚拟节点** @author feng*/
public class ConsistencyExcludeVirtualNodeHashAlgorithm implements HashAlgorithm {@Overridepublic SortedMap<String, List<String>> hash(Set<String> clients) {SortedMap<String, List<String>> resultMap = new TreeMap<>();// 哈希散列服务端地址SortedMap<Integer, String> hashServerMap = serverHash();// 处理客户端地址for (String client : clients) {int clientHashCode = Math.abs(client.hashCode());// 从服务端地址中查找能够处理的服务器节点,tailMap表示返回此映射中键大于或等于key的部分的mapSortedMap<Integer, String> tempSortedMap = hashServerMap.tailMap(clientHashCode);// 从当前节点,顺时针转完一整圈,回到0点,仍未发现能够处理的服务器if (tempSortedMap.isEmpty()) {// 取哈希环中的第一个服务器Integer firstKey = hashServerMap.firstKey();String firstAddress = hashServerMap.get(firstKey);System.out.printf("客户端%s 选中了服务端序号为%s,地址为%s \r\n", client, firstKey, firstAddress);List<String> newArrayList = resultMap.getOrDefault(firstAddress, new ArrayList<>());newArrayList.add(client);resultMap.put(firstAddress, newArrayList);} else {// 从哈希环取符合条件的服务器Integer firstKey = tempSortedMap.firstKey();String firstAddress = hashServerMap.get(firstKey);System.out.printf("客户端%s 选中了服务端序号为%s,地址为%s \r\n", client, firstKey, firstAddress);List<String> newArrayList = resultMap.getOrDefault(firstAddress, new ArrayList<>());newArrayList.add(client);resultMap.put(firstAddress, newArrayList);}}return resultMap;}private SortedMap<Integer, String> serverHash() {SortedMap<Integer, String> hashServerMap = new TreeMap<>();SERVER_ADDRESS_MAP.forEach((k, v) -> {int serverAddressHashCode = Math.abs(v.hashCode());hashServerMap.put(serverAddressHashCode, v);});return hashServerMap;}
}

5.4 一致性哈希算法(包含虚拟节点)

package com.example.demo;import java.util.*;/*** 一致性哈希算法-包括虚拟节点** @author feng*/
public class ConsistencyIncludeVirtualNodeHashAlgorithm implements HashAlgorithm {/*** 虚拟节点个数*/private final int virtualNodeCount;public ConsistencyIncludeVirtualNodeHashAlgorithm(int virtualNodeCount) {this.virtualNodeCount = virtualNodeCount;}@Overridepublic SortedMap<String, List<String>> hash(Set<String> clients) {SortedMap<String, List<String>> resultMap = new TreeMap<>();// 哈希散列服务端地址,增加虚拟节点映射SortedMap<Integer, String> hashServerMap = serverHash();for (String client : clients) {int clientHashCode = Math.abs(client.hashCode());// 从服务端地址中查找能够处理的服务器节点,tailMap表示返回此映射中键大于或等于key的部分的mapSortedMap<Integer, String> tempSortedMap = hashServerMap.tailMap(clientHashCode);// 从当前节点,顺时针转完一整圈,回到0点,仍未发现能够处理的服务器if (tempSortedMap.isEmpty()) {// 取哈希环中的第一个服务器Integer firstKey = hashServerMap.firstKey();String firstAddress = hashServerMap.get(firstKey);System.out.printf("客户端%s 选中了服务端序号为%s,地址为%s \r\n", client, firstKey, firstAddress);List<String> newArrayList = resultMap.getOrDefault(firstAddress, new ArrayList<>());newArrayList.add(client);resultMap.put(firstAddress, newArrayList);} else {// 从哈希环取符合条件的服务器Integer firstKey = tempSortedMap.firstKey();String firstAddress = hashServerMap.get(firstKey);System.out.printf("客户端%s 选中了服务端序号为%s,地址为%s \r\n", client, firstKey, firstAddress);List<String> newArrayList = resultMap.getOrDefault(firstAddress, new ArrayList<>());newArrayList.add(client);resultMap.put(firstAddress, newArrayList);}}return resultMap;}private SortedMap<Integer, String> serverHash() {SortedMap<Integer, String> hashServerMap = new TreeMap<>();SERVER_ADDRESS_MAP.forEach((k, v) -> {int serverAddressHashCode = Math.abs(v.hashCode());hashServerMap.put(serverAddressHashCode, v);// 增加虚拟节点for (int i = 0; i < virtualNodeCount; i++) {String virtualNodeName = v + "#" + i;int virtualNodeHashCode = Math.abs((virtualNodeName).hashCode());hashServerMap.put(virtualNodeHashCode, virtualNodeName);}});return hashServerMap;}
}

5.5 测试

package com.example.demo;import java.util.*;/*** 测试** @author feng*/
public class Client {static {HashAlgorithm.SERVER_ADDRESS_MAP.put(0, "11.123.32.3");HashAlgorithm.SERVER_ADDRESS_MAP.put(1, "23.43.41.1");HashAlgorithm.SERVER_ADDRESS_MAP.put(2, "192.169.21.3");}public static void main(String[] args) {Set<String> set = Set.of("10.78.12.3", "113.25.63.1", "126.12.3.8");SortedMap<String, List<String>> hash;// 简单hash算法hash = new GeneralHashAlgorithm(3).hash(set);hash.forEach((k, v) -> System.out.printf("简单hash算法-客户端地址%s已经映射到服务端%s \r\n", v, k));System.out.println();// 一致性hash算法(不包括虚拟节点)hash = new ConsistencyExcludeVirtualNodeHashAlgorithm().hash(set);hash.forEach((k, v) -> System.out.printf("一致性hash算法(不包括虚拟节点)-客户端地址%s已经映射到服务端%s \r\n", v, k));System.out.println();// 一致性hash算法(包括虚拟节点)hash = new ConsistencyIncludeVirtualNodeHashAlgorithm(3).hash(set);hash.forEach((k, v) -> {// 截取真实服务端地址String realServer = k;if (k.contains("#")) {realServer = k.substring(0, k.indexOf("#"));}System.out.printf("一致性hash算法(包括虚拟节点)-客户端地址%s已经映射到服务端%s ,真实的服务器地址是%s \r\n", v, k, realServer);});}
}

5.6 测试结果

客户端113.25.63.1 选中了服务端序号为2,地址为192.169.21.3 
客户端10.78.12.3 选中了服务端序号为1,地址为23.43.41.1 
客户端126.12.3.8 选中了服务端序号为1,地址为23.43.41.1 
简单hash算法-客户端地址[113.25.63.1]已经映射到服务端192.169.21.3 
简单hash算法-客户端地址[10.78.12.3, 126.12.3.8]已经映射到服务端23.43.41.1 客户端113.25.63.1 选中了服务端序号为1763606946,地址为192.169.21.3 
客户端10.78.12.3 选中了服务端序号为1763606946,地址为192.169.21.3 
客户端126.12.3.8 选中了服务端序号为47976546,地址为23.43.41.1 
一致性hash算法(不包括虚拟节点)-客户端地址[113.25.63.1, 10.78.12.3]已经映射到服务端192.169.21.3 
一致性hash算法(不包括虚拟节点)-客户端地址[126.12.3.8]已经映射到服务端23.43.41.1 客户端113.25.63.1 选中了服务端序号为1139178415,地址为23.43.41.1#2 
客户端10.78.12.3 选中了服务端序号为632380315,地址为11.123.32.3#0 
客户端126.12.3.8 选中了服务端序号为47976546,地址为23.43.41.1 
一致性hash算法(包括虚拟节点)-客户端地址[10.78.12.3]已经映射到服务端11.123.32.3#0 ,真实的服务器地址是11.123.32.3 
一致性hash算法(包括虚拟节点)-客户端地址[126.12.3.8]已经映射到服务端23.43.41.1 ,真实的服务器地址是23.43.41.1 
一致性hash算法(包括虚拟节点)-客户端地址[113.25.63.1]已经映射到服务端23.43.41.1#2 ,真实的服务器地址是23.43.41.1 

六、一致性哈希算法的应用场景

  • PC框架Dubbo用来选择服务提供者
  • 分布式关系数据库分库分表:数据与节点的映射关系
  • LVS负载均衡调度器
  • memcached 路由算法
  • redis 的hash槽

相关文章:

一致性哈希算法原理

文章目录 前言正文一、抛砖引玉&#xff0c;简单Hash算法的短板二、一致性Hash算法的基本概念2.1 一致性Hash算法本质也是取模2.2 便于理解的抽象&#xff0c;哈希环2.3 服务器如何映射到哈希环上2.4 对象如何映射到哈希环上&#xff0c;并关联到对应的机器 三、一致性Hash算法…...

回归预测 | MATLAB实现BO-LSTM贝叶斯优化长短期神经网络多输入单输出回归预测

回归预测 | MATLAB实现BO-LSTM贝叶斯优化长短期神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现BO-LSTM贝叶斯优化长短期神经网络多输入单输出回归预测效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 MATLAB实现BO-LSTM贝叶斯优化长短期神经网络多输入…...

工厂干洗店洗鞋店系统,校园洗护小程序来了

洗鞋店小程序&#xff0c;干洗店软件&#xff0c;洗护行业小程序,上门取衣小程序,预约干洗小程序,校园干洗店小程序,工厂干洗店小程序,干洗店小程序开发&#xff0c;成品软件开发 洗衣工厂软件、功能强大&#xff01; 包含以下主要功能&#xff1a; * 用户选择洗护用品&#x…...

计算机毕设 opencv 图像识别 指纹识别 - python

文章目录 0 前言1 课题背景2 效果展示3 具体实现3.1 图像对比过滤3.2 图像二值化3.3 图像侵蚀细化3.4 图像增强3.5 特征点检测 4 OpenCV5 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往…...

简化通知基础设施:开源的消息通知服务 | 开源专题 No.41

novuhq/novu Stars: 22.9k License: MIT Novu 是一个开源的通知基础设施项目&#xff0c;它提供了统一的 API 来通过多个渠道发送通知&#xff0c;包括应用内、推送、电子邮件、短信和聊天。主要功能有&#xff1a; 为所有消息提供商 (应用内、电子邮件、短信、推送和聊天) 提…...

微信公众号排版写作

对话框添加菜单 05 一节课学会使用微信自动回复_哔哩哔哩_bilibili 一件扫图关注 一件扫描付款 公众号排版 10元付费 08 —长按二维码关注和收款_哔哩哔哩_bilibili 微信开店 09 一节课开设自己的微店_哔哩哔哩_bilibili 知乎软文&#xff0c;设置引流矩阵 20 —在知乎写…...

UE4/5 批量进行贴图Texture压缩、修改饱和度

该插件下载地址&#xff1a; &#x1f35e;正在为您运送作品详情https://mbd.pub/o/bread/ZZWYmpxw 适用于 UE4 4.25/4.26/4.27 UE5 以上版本 在Edit - Plugins中分别开启 插件 Python Editor Script Plugin 插件 Editor Scripting Utilites 如果会python代码&#xff0c;…...

mysql中limit和offset的用法详细介绍

有的时候我们在学习或者工作中会使用到SQL语句&#xff0c;那么介绍一下limit和offset的使用方法。 mysql里分页一般用limit来实现&#xff0c;例如&#xff1a; 1、select* from user limit 3 表示直接取前三条数据 2、select * from user limit 1,3; 表示取1后面的第2,3,…...

vivado简单仿真入门

打开软件 创建工程 create project ![在这里插入图片描述](https://img-blog.csdnimg.cn/892eda626d394733920854b71ca8f726.png)先next,保留工程路径&#xff0c;配置环境 配置芯片环境 本次芯片类型 xc7k325tffg900-2 创建之后完整的demo 编写仿真内容 timescale 1ns/1…...

Elsevier (爱思唯尔) 期刊 投稿流程与注意点

&#x1f604; Elsevier (爱思唯尔) 期刊投稿流程中还是遇到了不少问题的&#xff0c;本篇文章总结一些说明文档和提交要点。 ⭐ LaTex 模板说明 & 投稿流程与准备 latex模版和投稿流程相关参考说明可看下面几个网址&#xff0c;总结的非常全面了&#xff1a; Elsevier&am…...

centos Let‘s Encrypt 免费https证书申请,并且自动续约

一、首先我们要使用certbot 工具 官网地址&#xff1a; https://certbot.eff.org/instructions?wsother&oscentosrhel8 下载 snap 工具 sudo yum install snapd sudo systemctl enable --now snapd.socket sudo ln -s /var/lib/snapd/snap /snap sudo systemctl status…...

nodejs+vue城市轨道交通线路查询系统-计算机毕业设计

着社会的快速发展&#xff0c;计算机的影响是全面且深入的。社会生产水平的不断提高&#xff0c;日常生活中人们对备忘记账系统方面的要求也在不断提高&#xff0c;因特网的使用越来越广泛&#xff0c;而在众多的因特网中&#xff0c;万维网更是为人们带来了新鲜的体验。在这当…...

MFC Windows 程序设计[332]之十进制转十六进制编辑框(附源码)

MFC Windows 程序设计[332]之十进制转十六进制编辑框 程序之美前言主体运行效果核心代码逻辑分析结束语程序之美 前言 MFC是微软公司提供的一个类库(class libraries),以C++类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。其中包…...

转化率的催化剂:网站客服机器人如何推动企业销售?

随着5G的推广&#xff0c;人工智能技术的普及程度越来越高&#xff0c;人机交互已经成为这个时代的常态&#xff0c;无论是在我们的日常生活中还是在企业服务中都非常常见。如今&#xff0c;无论是营销型企业还是客服型企业&#xff0c;都纷纷采用网站客服机器人服务&#xff0…...

Go 包操作之如何拉取私有的Go Module

Go 包操作之如何拉取私有的Go Module 在前面&#xff0c;我们已经了解了GO 项目依赖包管理与Go Module常规操作&#xff0c;Go Module 构建模式已经成为了 Go 语言的依赖管理与构建的标准。 在平时使用Go Module 时候&#xff0c;可能会遇到以下问题&#xff1a; 在某 modul…...

VR酒店专业情景教学演示

VR酒店情景教学为学生带来的全新学习体验。在这个虚拟环境中&#xff0c;学生可以亲身经历各种酒店管理场景&#xff0c;从客房清洁、餐厅服务&#xff0c;到客人接待、突发事件处理&#xff0c;都能得到生动的模拟和实践。 客房清洁是酒店管理中最基础却也最重要的一环。通过V…...

odps函数

1、wm_concat 聚合函数&#xff0c;可以实现对分组后的列数据拼接成一行。 参数&#xff1a;第一个参数为分隔符&#xff0c;第二个参数为要聚合的列&#xff1b; select prov_code,wm_concat(-,city_name) from code_china_area group by prov_code; 2、datediff 日期函数…...

【golang】mysql默认排序无法实现 使用golang实现对时间字符串字段的排序

一、问题场景 1、mysql实现排序-性能低下 例如&#xff1a;某字段 finish_time 数据如下&#xff1a;6:13:27、 10:56:11、 21:56:11 会出现顺序如下的场景&#xff1a; 10:56:11、 21:56:11、6:13:27 二、解决方案 2、golang实现排序 package mainimport ("fmt"&…...

C语言学习笔记总结(一)

C语言基础 字节大小 char&#xff1a;1 字节 unsigned char&#xff1a;1 字节 short&#xff1a;2 字节 unsigned short&#xff1a;2 字节 int&#xff1a;通常为 4 字节&#xff08;32 位平台&#xff09;或 8 字节&#xff08;64 位平台&#xff09; unsigned int&#x…...

WPF:自定义按钮模板

1.WPF:自定义按钮模板 自定义封装的按钮属性可写在<Button.Template>中 Background"{TemplateBinding Background}"中的TemplateBinding代表使用按钮本身所使用的背景颜色 不在样式内修改背景颜色 例如&#xff1a; <Button Width"300" Height&q…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

免费数学几何作图web平台

光锐软件免费数学工具&#xff0c;maths,数学制图&#xff0c;数学作图&#xff0c;几何作图&#xff0c;几何&#xff0c;AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践

在 Kubernetes 集群中&#xff0c;如何在保障应用高可用的同时有效地管理资源&#xff0c;一直是运维人员和开发者关注的重点。随着微服务架构的普及&#xff0c;集群内各个服务的负载波动日趋明显&#xff0c;传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...

游戏开发中常见的战斗数值英文缩写对照表

游戏开发中常见的战斗数值英文缩写对照表 基础属性&#xff08;Basic Attributes&#xff09; 缩写英文全称中文释义常见使用场景HPHit Points / Health Points生命值角色生存状态MPMana Points / Magic Points魔法值技能释放资源SPStamina Points体力值动作消耗资源APAction…...

云原生安全实战:API网关Envoy的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口&#xff0c;负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...

Spring是如何实现无代理对象的循环依赖

无代理对象的循环依赖 什么是循环依赖解决方案实现方式测试验证 引入代理对象的影响创建代理对象问题分析 源码见&#xff1a;mini-spring 什么是循环依赖 循环依赖是指在对象创建过程中&#xff0c;两个或多个对象相互依赖&#xff0c;导致创建过程陷入死循环。以下通过一个简…...