一致性哈希算法原理

news2024/11/13 9:42:27

文章目录

  • 前言
  • 正文
    • 一、抛砖引玉,简单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;
    }

    @Override
    public 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 {

    @Override
    public 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的部分的map
            SortedMap<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;
    }

    @Override
    public 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的部分的map
            SortedMap<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槽

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

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

相关文章

基于Java的民宿酒店预约推广系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09; 代码参考数据库参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…

腾讯云服务器价格计算器,它来了!

腾讯云推出云服务器CVM专用计算器&#xff0c;选择云服务器CPU内存、CVM实例规格、系统盘、公网带宽即可一键计算出精准报价&#xff0c;腾讯云价格计算器&#xff0c;它来了&#xff0c;它来了&#xff01; 腾讯云服务器价格计算器可以一键计算出云服务器的精准报价&#xff…

什么是外贸电商ERP?外贸电商ERP有哪些

得益于互联网技术的发展成熟&#xff0c;外贸电商行业也迎来新的发展机遇&#xff0c;不过由于行业的特殊性&#xff0c;涉及多渠道营销、多汇率核算、多税制、多语言、多货币等情况&#xff0c;随着业务量的增加&#xff0c;相关数据的统计分析工作也愈加繁重。 外贸电商采用…

git的实际操作

文章目录 删除GitHub上的某个文件夹克隆仓库到另一个仓库 删除GitHub上的某个文件夹 克隆仓库到另一个仓库 从原地址克隆一份裸板仓库 –bare创建的克隆版本库都不包含工作区&#xff0c;直接就是版本库的内容&#xff0c;这样的版本库称为裸版本库 git clone --bare ****(原…

QT mqtt 在子线程中使用

qtmqtt 在子线程中使用_qt在子线程里mqtt无法new-CSDN博客文章浏览阅读524次。解决问题&#xff1a;QMqttClient - connection not made from another thread在qt中使用多线的qtmqtt客户端发送接收数据_qt在子线程里mqtt无法newhttps://blog.csdn.net/qq_35708970/article/deta…

2023年中国高尔夫球杆市场供需现状及趋势,量身定制会逐渐成为一种趋势[图]

随着高尔夫运动的发展&#xff0c;高尔夫球杆也在不断更新。20世纪80年代是高尔夫球杆设计与发展的黄金时期&#xff0c;大部分经典的球杆都是从这个时期被设计发明出来。高尔夫球杆的技术革新主要集中在杆头上&#xff0c;通过杆头的变化来提高和改善击球的效果。对应不同击球…

为小团队(20人以下)推荐的最佳网盘选择

现在20人以下的小团队之间对于文件存储、团队文件协作的需求也越来越高。之前通过社交工具如微信、QQ等进行文件共享的方式已经无法满足小团队的需求。而网盘是当下热门的文件存储及共享工具&#xff0c;相比于NAS&#xff0c;网盘具备成本较低、协作性更强的特点。那么有什么适…

视频批量AI智剪:提升剪辑效率的秘密方法

随着视频内容的爆炸式增长&#xff0c;剪辑师面临着巨大的工作压力。传统的剪辑方法往往效率低下&#xff0c;无法满足快速、高质量的剪辑需求。为了解决这个问题&#xff0c;视频批量AI智剪技术应运而生&#xff0c;成为提升剪辑效率的秘密方法。 视频批量AI智剪是指利用人工…

红路灯识别

1.截图图像中的目标对象 1.1 查找边界矩形 1.1.1直边界矩形 一个直矩形&#xff08;就是没有旋转的矩形&#xff09;。它不会考虑对象是否旋转。所以边界矩形的面积不是最小的。 cv2.boundingRect(array) 输入&#xff1a;点集 输出&#xff1a;&#xff08;x&#xff0c;y…

领域高口碑 | 中科院1区TOP,Elsevier出版社,仅1个月Accept!稳定检索40年!

【SciencePub学术】本期&#xff0c;小编给大家推荐的是一本Elsevier旗下、稳定检索40年、影响因子为10.0的中科院1区TOP刊&#xff0c;期刊详情及专题信息如下&#xff1a; 期刊简介 COMPUTERS IN INDUSTRY ISSN&#xff1a;0166-3615 E-ISSN&#xff1a;1872-6194 IF&a…

第二证券:全产业链迎利好 大基建板块掀涨停潮

受增发国债利好音讯提振&#xff0c;大基建板块25日迎来爆发&#xff0c;水利、水泥制造、基建、工程器械、钢铁、修建等方向全线大涨&#xff0c;多只股票触及涨停。业内人士以为&#xff0c;进行年内预算调整并增发国债&#xff0c;将对A股商场投资者的整体心境有所提振。 大…

宝塔面板 - 404 Not Found(Nginx)

面板提示 404 可能是丢失面板安全入口&#xff0c;具体您可以在服务器命令下先执行 bt&#xff0c;然后选择 14 命令重新获取面板URL地址 安全入口验证信息&#xff0c;重新登录就行。

python自动化测试(二):xpath获取元素

目录 前置代码 一、什么是xpath方式 二、通过xpath 单组属性名属性值 的方式进行元素定位 三、通过xpath的多组属性进行元素的定位 四、通过xpath文本值的方式进行元素定位 五、通过模糊的文本值方式进行元素定位 前置代码 # codingutf-8 from selenium import webdrive…

第88步 时间序列建模实战:ARIMA组合模型

基于WIN10的64位系统演示 一、写在前面 这一期&#xff0c;我们介绍基于ARIMA的组合模型。 &#xff08;1&#xff09;数据源&#xff1a; 《PLoS One》2015年一篇题目为《Comparison of Two Hybrid Models for Forecasting the Incidence of Hemorrhagic Fever with Renal…

电脑f盘文件凭空消失了怎么恢复?别慌,这里有五种方法

在使用电脑的过程中&#xff0c;我们可能会遇到F盘文件突然凭空消失的情况&#xff0c;这不仅会给我们带来困扰&#xff0c;而且可能造成重要文件的丢失。但是&#xff0c;即使文件消失了&#xff0c;我们仍然有可能通过一些方法恢复这些文件。本文将介绍如何有效恢复电脑F盘文…

【AICFD案例操作】溃坝过程模拟

AICFD是由天洑软件自主研发的通用智能热流体仿真软件&#xff0c;用于高效解决能源动力、船舶海洋、电子设备和车辆运载等领域复杂的流动和传热问题。软件涵盖了从建模、仿真到结果处理完整仿真分析流程&#xff0c;帮助工业企业建立设计、仿真和优化相结合的一体化流程&#x…

2023年【安徽省安全员C证】考试技巧及安徽省安全员C证复审模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年【安徽省安全员C证】考试技巧及安徽省安全员C证复审模拟考试&#xff0c;包含安徽省安全员C证考试技巧答案和解析及安徽省安全员C证复审模拟考试练习。安全生产模拟考试一点通结合国家安徽省安全员C证考试最新大…

数据结构之队列(源代码➕图解➕习题)

前言 在学过栈之后&#xff0c;会了解到栈的底层是根据顺序表或者链表来构建的&#xff0c;那么我们今天要学习的队列是否也是基于顺序表和链表呢&#xff1f;那我们直接进入正题吧&#xff01; 1. 队列的概念&#xff08;图解&#xff09; 还是跟上节一样&#xff0c;依旧用图…

生成式人工智能在软件开发过程现代化中的作用

集成人工智能的独特功能可以在软件开发生命周期(SDLC)的每个阶段为开发人员提供帮助&#xff0c;从业务需求分析和创建敏捷的用户故事到软件设计、编码、测试、部署、监控和维护。这是组织可以使用生成式人工智能进行优化的地方。 近年来&#xff0c;软件工程见证了向增强自动化…

g++ 命令行编译

一.创建项目并编译和执行 要创建的项目的结构 创建main.cpp # 创建一个项目 sudo mkdir GCC_Demo_swap# 进入该项目 cd GCC_Demo_swap # 创建该项目的主程序 sudo vim main.cpp#include <iostream> #include "swap.h" using namespace std;int main(int ar…