Redis:分布式 - 集群

Redis:分布式 - 集群

    • 集群
    • 数据分片
      • 哈希求余
      • 一致性哈希算法
      • 哈希槽分区算法
    • Docker搭建集群
    • 集群操作
      • 重定向
      • 故障转移
      • 集群扩容


集群

主从复制哨兵模式中,数据库的数据对于每一台主机来说,都是全量保存的。这就会导致,就算引入再多台服务器,数据的存储上限都不会变。为了提高数据的存储上限,那么就要让每个节点都保存一部分数据,而不是保存所有数据。在Redis中,这种模式称为集群

此处要辨析一下集群的概念:

  • 广义集群:只要是多台服务器共同提供服务,就称为集群
  • Redis集群:不同服务器只存储总数据的一部分

每一部分数据,不能由一个节点来存储,一旦这个节点宕机,那么这部分数据就会丢失,因此每一部分数据,都会用一个主从复制来维护。

如下图:

在这里插入图片描述

此处维护了三个主从复制,每个主从复制都只存储总量1 / 3的数据,这样既可以提高存储的数据总量,又可以保证数据的安全。

此处每一部分数据,都称为一个分片,接下来讲解如何对数据进行分片。


数据分片

分片的核心思路是用多组机器来存数据的每个部分,那么接下来的核心问题就是,给定一个数据(一个具体的 key),那么这个数据应该存储在哪个分片上?读取的时候又应该去哪个分片读取?围绕这个问题,业界有三种比较主流的实现方式:

  1. 哈希求余
  2. 一致性哈希算法
  3. 哈希槽分区算法

哈希求余

第一种是采用最基本的哈希算法,将key通过哈希算法转化为一个哈希值,随后对哈希值进行取模运算。

h a s h ( k e y )   m o d   N hash(key) \bmod N hash(key)modN

hash(key)用于取哈希值,N是分区个数,上式结果是多少,那么该数据就放入到第几个分区存储。

这种方式非常简单有效,但是也会遇到问题,那就是扩容问题。扩容后N的值会变大,那么原先的求余就不再适用了,此时要把所有数重新计算。计算完毕后,还要面临复杂的交换数据的过程。


一致性哈希算法

一致性哈希算法解决了数据拷贝的问题,如下图:

在这里插入图片描述

假设最开始有四个分片,经过哈希函数的到达哈希值一定在 [ 0 , 2 32 ) [0, 2^{32}) [0,232)区间内,那么将 2 32 2^{32} 232个哈希值均匀分成四份。每个范围内的数据,都属于一个分区,每个分区存储的哈希值范围都是 2 30 2^{30} 230

当要进行扩容,加入新的分片:

在这里插入图片描述

如图所示,直接从某一个分片中划分出一半来,紫色区域就是新的分片需要存储的数据范围。这样,就只需要从原先的绿色分片中,拷贝出大约一半的数据给新分区。一方面来说,拷贝是一对一的,没有错综复杂的拷贝关系,另一方面,拷贝的数据变少了,这样就可以节省很多拷贝的消耗。

但是这也会面临一个问题,那就是加入新分区后,五个分区的数据量不同了,这称为数据倾斜


哈希槽分区算法

前两个算法都存在一定的缺陷,而哈希槽分区算法解决了这些算法,最终Redis采用了这一版算法。

Redis官方文档给出了以下公式:

c r c 16 ( k e y )   m o d   16384 crc16(key) \bmod 16384 crc16(key)mod16384

此处的crc是一种哈希算法,而16384 = 1024 * 16,这样就生成了16384个哈希槽,随后会把这些哈希槽分配给不同的分片。

假设当前有三个分片,它们分配到的哈希槽可能为:

  • 0号分片[0, 5461],共5462个槽位
  • 1号分片[5462, 10923],共5462个槽位
  • 2号分片[10924, 16383],共5460个槽位

也就是说,每个分片最终会拿到尽可能接近的槽位,当crc(key)的值落在对应的槽位,那么这个数据就会进入指定的分片存储。

Redis内部,每个分片使用一张位图来表示自己持有的槽位,16384个槽位,只需要2048个字节来表示即可。

当然这些槽位的编号未必是连续的,也有可能是其它分配方法,但是最终还是要保证每个分片的槽位的个数接近。

当对数据库扩容,新增一个分片,此时就会对槽位再分配:

  • 0号分片[0, 4095],共4096个槽位
  • 1号分片[5462, 9557],共4096个槽位
  • 2号分片[10924, 15019],共4096个槽位
  • 3号分片[4096, 5461] + [9558, 10923] + [15020, 16383],共4096个槽位

此时,每个分片分出一部分槽位给新来的分片,并且保证最终所有分片持有的槽位个数接近,此处因为16384 / 4可以除尽,所以最终四个分片的槽位数目完全相同。

最后进行数据拷贝时,只需要0 1 2三个分片,分别单向拷贝一部分数据给3号分片即可。

  • 为什么是16384个槽位?
  1. 节点之间通过心跳包通信,心跳包中包含了该节点持有哪些槽位,这个是使用位图这样的数据结构表示的。表示 16384 个槽位需要的位图大小是 2KB,如果给定的槽位数更多了,此时就需要消耗更多的空间。这对于内存来说不算什么,但是在频繁的网络心跳包中,还是一个不小的开销,因为网络带宽是比内存更稀缺的资源
  2. 另一方面,Redis 集群一般不建议超过 1000 个分片。16384个槽位,对于最大 1000 个分片来说是足够用的,同时也会使对应的槽位配置位图体积不至于很大

Docker搭建集群

接下来使用docker搭建一个如下集群:

在这里插入图片描述

将整个数据库分为三个分片,每个分片都有一个主从复制。除了这九个节点,还需要额外的两个节点来进行模拟扩容操作,但是最开始不加入集群,是两个单独的节点。

另外的,为了方便操作,每一个节点都指定一个内网IP,后续直接通过操作IP来操作redis

首先挑选一个合适的目录,用于进行测试,在目录中写如下脚本文件generate.sh

for port in $(seq 1 9); \
do \
mkdir -p redis${port}/
touch redis${port}/redis.conf
cat << EOF > redis${port}/redis.conf
port 6379
bind 0.0.0.0
protected-mode no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.30.0.10${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
EOF
done

for port in $(seq 10 11); \
do \
mkdir -p redis${port}/
touch redis${port}/redis.conf
cat << EOF > redis${port}/redis.conf
port 6379
bind 0.0.0.0
protected-mode no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.30.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
EOF
done

这个脚本可以生成十一个目录,每个目录内部都有一个redis配置文件。

执行bash generate.sh

在这里插入图片描述

此时就生成了是一个配置文件与目录了。

编写docker-compose.yml

networks:
  mynet:
    ipam:
      config:
        - subnet: 172.30.0.0/24

services:
  redis1:
    image: 'redis:5.0.9'
    container_name: redis1
    restart: always
    volumes:
      - ./redis1/:/etc/redis/
    ports:
      - 6371:6379
      - 16371:16379
    command:
      redis-server /etc/redis/redis.conf
    networks:
      mynet:
        ipv4_address: 172.30.0.101
  redis2:
    image: 'redis:5.0.9'
    container_name: redis2
    restart: always
    volumes:
      - ./redis2/:/etc/redis/
    ports:
      - 6372:6379
      - 16372:16379
    command:
      redis-server /etc/redis/redis.conf
    networks:
      mynet:
        ipv4_address: 172.30.0.102
  redis3:
    image: 'redis:5.0.9'
    container_name: redis3
    restart: always
    volumes:
      - ./redis3/:/etc/redis/
    ports:
      - 6373:6379
      - 16373:16379
    command:
      redis-server /etc/redis/redis.conf
    networks:
      mynet:
        ipv4_address: 172.30.0.103
  redis4:
    image: 'redis:5.0.9'
    container_name: redis4
    restart: always
    volumes:
      - ./redis4/:/etc/redis/
    ports:
      - 6374:6379
      - 16374:16379
    command:
      redis-server /etc/redis/redis.conf
    networks:
      mynet:
        ipv4_address: 172.30.0.104
  redis5:
    image: 'redis:5.0.9'
    container_name: redis5
    restart: always
    volumes:
      - ./redis5/:/etc/redis/
    ports:
      - 6375:6379
      - 16375:16379
    command:
      redis-server /etc/redis/redis.conf
    networks:
      mynet:
        ipv4_address: 172.30.0.105
  redis6:
    image: 'redis:5.0.9'
    container_name: redis6
    restart: always
    volumes:
      - ./redis6/:/etc/redis/
    ports:
      - 6376:6379
      - 16376:16379
    command:
      redis-server /etc/redis/redis.conf
    networks:
      mynet:
        ipv4_address: 172.30.0.106
  redis7:
    image: 'redis:5.0.9'
    container_name: redis7
    restart: always
    volumes:
      - ./redis7/:/etc/redis/
    ports:
      - 6377:6379
      - 16377:16379
    command:
      redis-server /etc/redis/redis.conf
    networks:
      mynet:
        ipv4_address: 172.30.0.107
  redis8:
    image: 'redis:5.0.9'
    container_name: redis8
    restart: always
    volumes:
      - ./redis8/:/etc/redis/
    ports:
      - 6378:6379
      - 16378:16379
    command:
      redis-server /etc/redis/redis.conf
    networks:
      mynet:
        ipv4_address: 172.30.0.108
  redis9:
    image: 'redis:5.0.9'
    container_name: redis9
    restart: always
    volumes:
      - ./redis9/:/etc/redis/
    ports:
      - 6379:6379
      - 16379:16379
    command:
      redis-server /etc/redis/redis.conf
    networks:
      mynet:
        ipv4_address: 172.30.0.109
  redis10:
    image: 'redis:5.0.9'
    container_name: redis10
    restart: always
    volumes:
      - ./redis10/:/etc/redis/
    ports:
      - 6380:6379
      - 16380:16379
    command:
      redis-server /etc/redis/redis.conf
    networks:
      mynet:
        ipv4_address: 172.30.0.110
  redis11:
    image: 'redis:5.0.9'
    container_name: redis11
    restart: always
    volumes:
      - ./redis11/:/etc/redis/
    ports:
      - 6381:6379
      - 16381:16379
    command:
      redis-server /etc/redis/redis.conf
    networks:
      mynet:
        ipv4_address: 172.30.0.111

由于要启动是一个容器,所以这个文件会比较长。

启动容器:

docker compose up -d

在这里插入图片描述

此时十一个容器就启动了。

但是这些容器目前还是单独的节点,尚未构成集群。

执行以下命令完成集群构建:

redis-cli --cluster create 172.30.0.101:6379 172.30.0.102:6379 172.30.0.103:6379 172.30.0.104:6379 172.30.0.105:6379 172.30.0.106:6379 172.30.0.107:6379 172.30.0.108:6379 172.30.0.109:6379 --cluster-replicas 2
  • --cluster create:表示建立集群,后面填写每个节点的IP和端口
  • --cluster-replicas 2:表示每个主节点需要两个从节点

这样redis就会依据规则自动构建集群,每三个节点构成一个分片(因为指定了一个主有两个从),并且会自动构建主从关系。

输入命令后,出现以下界面:

在这里插入图片描述

Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383

这一部分,就是信号槽的分配部分,可以看到redis内部采用了连续分配的方式,将信号槽均匀的分配给三个分片。

Adding replica 172.30.0.105:6379 to 172.30.0.101:6379
Adding replica 172.30.0.106:6379 to 172.30.0.101:6379
Adding replica 172.30.0.107:6379 to 172.30.0.102:6379
Adding replica 172.30.0.108:6379 to 172.30.0.102:6379
Adding replica 172.30.0.109:6379 to 172.30.0.103:6379
Adding replica 172.30.0.104:6379 to 172.30.0.103:6379

这是主从关系的建立部分,第一行表示:172.30.0.105:6379成为172.30.0.101:6379的从节点。那么这六行日志,就表示xxx.101xxx.102xxx.103成为了三个主节点,其余的节点成为这三个的从节点。

M: b2623df997bc620972a7a682852b16e1403e2f76 172.30.0.101:6379
   slots:[0-5460] (5461 slots) master
M: 490c33d0d8b9d3be62f56d069419af35e96525a7 172.30.0.102:6379
   slots:[5461-10922] (5462 slots) master
M: f84179e62de60218aa585fcc1c414ac25e0b85a0 172.30.0.103:6379
   slots:[10923-16383] (5461 slots) master

这一段日志,就是具体把那一部分槽位具体分配给哪一个分片,比如xxx.101拿到了[0-5460]的槽位号。

此时输入yes,表示用户同意这个构建集群的方式,redis就会开始构建集群。


集群操作

此时可以连接任意一个端口的redis,比如6371

redis-cli -p 6371

此时不论连接上任意一个节点,都可以视作连接上了整个集群。

cluster nodes

执行以上命令,可以看到当前节点集群内的所有节点。

在这里插入图片描述


重定向

尝试插入数据:

在这里插入图片描述

报错了,因为当前集群发生了分片,每个分片都只能存储一部分数据,key1经过哈希运算,发现并不是这个节点可以存储的值,于是就报错了。

想要解决这个问题,可以在启动时加上-c选项,此时插入数据,会自动重定向到对应的节点。

redis-cli -c -p 6371

在这里插入图片描述

如图,每次操作数据是,如果该数据不属于当前分片,就会触发一次重定向,自动跳转到对应的客户端。命令行前面的端口号一直在改变,这就说明我们的客户端一直在切换。

但是redis中,有一些命令同时操作多个key,比如最后一个命令mget,此时又报错了。因为这几个key属于不同分片,那么就无法同时处理,因此在集群的情况下,最好不要一次性操作多个key


故障转移

如果在集群中,某一个分片的主节点宕机了,会发生什么?在部署集群时,并没有引入哨兵节点,但是集群也会完成哨兵的工作,如果主节点宕机了,集群会自动完成重新选主的过程

如图:

在这里插入图片描述

首先通过docker stop redis1,关掉了redis1节点,也就是xxx.101下线了,而这是一个主节点。登录6372端口的客户端,查看当前集群,可以发现xxx.106成为了新的主节点,而xxx.106原先是xxx.101的从节点。

在这里插入图片描述

重启redis1,其变为了reids6的从节点。

此处集群的故障转移,和哨兵的故障转移是有一些差别的,接下来就讲解集群中是如何完成故障转移的。

  • 故障判定

集群中的所有节点,都会周期性的使用心跳包进行通信

  1. 节点A 给 节点B 发送ping包,B 就会给 A 返回一个pong包,pingpong除了消息类型属性之外,其他部分都是一样的,这里包含了集群的配置信息:
    • 节点的id
    • 该节点从属于哪个分片
    • 是主节点还是从节点
    • 从属于谁
    • 持有哪些哈希槽的位图
  2. 每个节点,每秒钟都会给一些随机的节点发起 ping 包,而不是全发一遍,这样设定是为了避免在节点很多的时候,心跳包也非常多
  3. 当 节点A 给 节点B 发起 ping包,B不能如期回应的时候,此时 A 就会尝试重置和 B 的 tcp 连接,看能否连接成功,如果仍然连接失败,A 就会把 B 设为 PFAIL 状态,相当于主观下线
  4. A 判定 B为 PFAIL 之后,会通过 redis 内置的 Gossip 协议,和其他节点进行沟通,向其他节点确认 B的状态,每个节点都会维护一个自己的"下线列表",由于视角不同,每个节点的下线列表也不一定相同
  5. 此时A发现其他很多节点也认为B为 PFAIL,并且数目超过总集群个数的一半,那么A就会把B标记成 FAIL (相当于客观下线),并且把这个消息同步给其他节点,其他节点收到之后,也会把B标记成FAIL

至此,B 就彻底被判定为故障节点了。

  • 故障迁移

上述例子中,B 故障并且 A 把 B FAIL 的消息告知集群中的其他节点:

  • 如果 B 是从节点,那么不需要进行故障迁移
  • 如果 B是主节点,那么就会由 B 的从节点触发故障迁移

所谓故障迁移,就是指把从节点提拔成主节点,继续给整个redis 集群提供支持.具体流程如下:

  1. 从节点判定自己是否具有参选资格,如果从节点和主节点已经太久没通信,此时认为从节点的数据和主节点差异太大了,时间超过阈值,就失去竞选资格
  2. 具有资格的节点,就会先休眠一定时间,休眠时间=500ms基础时间+[0,500ms]随机时间+排名*1000msoffset 的值越大,则排名越靠前(越小)
  3. 如果某个节点的休眠时间到了,该节点就会给其他所有集群中的节点,进行拉票操作,但是只有主节点才有投票资格
  4. 每个主节点只有1票,当该节点收到的票数超过主节点数目的一半,就会晋升成主节点
  5. 新的主节点自己负责执行 slaveofno one,并且让同一分片中的其它节点执行 slaveof
  6. 最后,新的主节点会把自己成为主节点的消息,同步给其他集群的节点,大家也都会更新自己保存的集群结构信息

以上算法成为raft算法,其实和哨兵选主的目的是一样的,就是选出那个目前网络状态比较好的节点成为主节点,而网络状态的反映,就是休眠时间。

有些情况下,如果节点宕机,会导致整个集群宕机,这称为fail状态:

  1. 某个分片内部,所有的主节点和从节点都挂了
  2. 某个分片内部,主节点挂了没有从节点可以成为新的主节点
  3. 超过半数的主节点都挂了

集群扩容

  • 加入集群

想要给集群扩容,可以通过--cluster add-node选项:

redis-cli --cluster add-node 新增节点 集群任意节点
  • 新增节点:要增加到集群的节点
  • 集群任意节点:用于标识要加入哪一个集群

执行:

redis-cli --cluster add-node 172.30.0.110:6379 172.30.0.101:6379

这样就可以把xxx.110节点加入到集群中,登入任意客户端查看:

在这里插入图片描述

可以看到,集群内部已经有xxx.110了,而且是一个主节点。但是仔细观察,可以发现其他节点末尾都有哈希槽的范围,但是新增的节点没有,说明新节点还没有分配。

  • 分配哈希槽

接下来就要给新节点分配哈希槽,同选项 --cluster reshard,注意是shard不是shared

redis-cli --cluster reshard 172.30.0.101:6379

执行后进入如下选项:

在这里插入图片描述

上面的S表示从节点,M表示主节点,在主节点的信息中,已经告知了每个主节点拥有的槽位个数。此处它询问要移动多少个slots,也就是哈希槽。

此处要移动4096个哈希槽给新节点,所以输入4096

在这里插入图片描述

随后它询问将这些哈希槽移动给哪一个节点,此时往上找哪一个master节点的哈希槽为0,复制他的ID。

在这里插入图片描述

最后询问,要从哪些节点中空出这些节点。如果选择all,那么就是从所有的现有节点平均提取。如果你希望自己指定,那么就复制那些节点的ID,最后以done结尾即可。

在这里插入图片描述

最后向用户确认,是否要这样执行。

最后进入任意客户端,查看集群现状:

在这里插入图片描述

可以看到,新节点获得了三个范围的哈希槽。

这里有一个小问题,这个搬运哈希槽的过程是比较久的,如果在搬运期间,用户访问数据是合法的吗?

这分情况,搬运过程中,大部分哈希槽是不用搬运的,如果用户访问这些哈希槽内的数据,那么可以正常访问。但是如果用户访问正在移动的哈希槽,那么就会失败了。

  • 添加从节点

目前添加了主节点,最后还要把从节点安排上,这通过 add-node命令配合--cluster-master-id完成:

redis-cli --cluster add-node 新节点 --cluster-slave --cluster-master-id 主节点的ID
  • --cluster-slave:这个选项指定新添加的节点将作为从节点
  • --cluster-master-id:这个选项后面跟着的是主节点的ID,表示该节点从属于哪一个节点

在这里插入图片描述

此时从属节点就成功加入集群了。


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity网络开发基础 —— 实践小项目

概述 接Unity网络开发基础 导入基础知识中的代码 需求分析 手动写Handler类 手动书写消息池 using GamePlayer; using System; using System.Collections; using System.Collections.Generic; using UnityEngine;/// <summary> /// 消息池中 主要是用于 注册 ID和消息类…

ps提示不能使用移动工具,因为目标通道被隐藏的解决办法

解决&#xff1a;按F7&#xff0c;或者从窗口把图层打开 按图示找到快速蒙版图层。它可能被隐藏或以特殊图标显示。右键删除或者拖到右下角垃圾桶里

岩石分类检测数据集 4700张 岩石检测 带标注 voc yolo 9类

岩石分类检测数据集 4700张 岩石检测 带标注 voc yolo 9类 岩石分类检测数据集 (Rock Classification and Detection Dataset) 描述: 本数据集旨在支持对不同类型的岩石进行自动分类和检测&#xff0c;特别适用于地质勘探、矿物识别、环境监测等领域。通过使用该数据集训练的模…

智慧云党建”主题网站设计与实现(源码+定制+开发)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

5G NR UE初始接入信令流程

文章目录 5G NR UE初始接入信令流程 5G NR UE初始接入信令流程 用户设备向gNB-DU发送RRCSetupRequest消息。gNB-DU 包含 RRC 消息&#xff0c;如果 UE 被接纳&#xff0c;则在 INITIAL UL RRC MESSAGE TRANSFER 消息中包括为 UE 分配的低层配置&#xff0c;并将其传输到 gNB-CU…

Sounding Reference Signal (SRS)

文章目录 探测参考信号&#xff08;SRS&#xff09;Sounding Reference Signals 探测参考信号的生成基序列 探测参考信号&#xff08;SRS&#xff09; 探测参考信号&#xff08;SRS&#xff09;在上行链路中传输&#xff0c;使网络能够估计不同频率下的信道质量。 Sounding R…

2024双十一值得购买的好物有哪些?看完这五款好物让你不后悔!

随着一年一度的双十一购物狂欢节即将拉开帷幕&#xff0c;作为一名热衷于分享购物心得的博主&#xff0c;我今天特别想在这里为大家详细介绍五款我个人非常期待入手的好物。这些产品都是经过我精心挑选和试用的&#xff0c;我相信它们不仅能够满足我的需求&#xff0c;同样也能…

使用HTML和CSS实现3D波浪动画效果

使用HTML和CSS实现3D波浪动画效果 在本篇博客中&#xff0c;将详细介绍如何使用HTML与CSS创建一个3D波浪动画效果。这个效果不仅能够在网页中创建立体感强的视觉体验&#xff0c;还能够通过悬停和聚焦实现与用户的交互。我们将逐步解析代码中的每个部分&#xff0c;帮助你掌握…

Stm32+Esp8266连接阿里云程序移植教程(MQTT协议)

Stm32Esp8266连接阿里云程序移植教程&#xff08;MQTT协议&#xff09; 一、前期准备二、移植过程三、程序的使用3.1 连接上阿里云3.2 传输用户数据到阿里云3.3 解析从阿里云下发给用户的数据3.4 关于调试接口 一、前期准备 自己要的工程文件移植所需的文件&#xff08;如下图&…

[ACTF2020] 新生赛]Exec1

目录 0x01命令执行 [ACTF2020 新生赛]Exec1 1、解法1 2、解法2 3、总结 3.1php命令注入函数 3.2java命令注入函数 3.3常见管道符 0x02SQL注入 [极客大挑战 2019]EasySQL1 0x01命令执行 [ACTF2020 新生赛]Exec1 1、解法1 ping本地&#xff0c;有回显&#xff0c;TTL…

数据分析Python for Data专业书籍pdf

随着大数据时代的到来&#xff0c;数据分析已成为企业决策、科学研究以及日常生活中不可或缺的一部分。对于想要学习数据分析的人来说&#xff0c;Python 是最理想的编程语言之一。其简洁的语法、强大的库以及丰富的社区支持&#xff0c;使得 Python 成为数据分析的首选工具。本…

隐蔽的并发错误

欢迎关注公众号 【11来了】 &#xff0c;持续 中间件源码、系统设计、面试进阶相关内容 在我后台回复 「资料」 可领取 编程高频电子书&#xff01; 在我后台回复「面试」可领取 30w 字的硬核面试笔记&#xff01; 感谢你的关注&#xff01; 隐蔽的 synchronized 并发错误 在使…

基于SpringBoot+Vue+Uniapp汽车保养系统小程序的设计与实现

详细视频演示 请联系我获取更详细的演示视频 项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而…

PROFINET转SSI协议模块快速使用说明

Profinet网关PROFINET转SSI协议 PN4SSI模块快速使用说明 * 应用时PNSSI模块时&#xff0c;拨码全部拨到OFF。 (1) 在博途中新建一个项目&#xff0c;通过“选项”——“管理通用站描述文件”添加模块的GSD文件。 选择GSD文件所在的目录&#xff0c;点击安装&#xff1a; (…

天选销冠徐志胜与森马的跨界营销,你get了吗?

在当今这个信息爆炸的时代&#xff0c;品牌如何在众多竞争者中脱颖而出&#xff0c;成为消费者心中的首选&#xff0c;是一个值得深入探讨的问题。知名脱口秀演员徐志胜携手森马推出《绒毛的歌》广告片&#xff0c;以其独特的幽默风格&#xff0c;试图为冬季羽绒服市场注入一股…

CEEMDAN +组合预测模型(Transformer - BiLSTM + ARIMA)

往期精彩内容&#xff1a; 时序预测&#xff1a;LSTM、ARIMA、Holt-Winters、SARIMA模型的分析与比较 全是干货 | 数据集、学习资料、建模资源分享&#xff01; EMD、EEMD、FEEMD、CEEMD、CEEMDAN的区别、原理和Python实现&#xff08;一&#xff09;EMD-CSDN博客 EMD、EEM…

解决linux服务器磁盘占满问题(详细,有效,100%解决)

应用场景&#xff1a; 在我们的日常开发中&#xff0c;我们的服务器总是在不知不觉中磁盘莫名奇妙少了很多空间&#xff0c;或者被占满了&#xff0c;如果这时候要想要存储什么文件&#xff0c;突然发现空间不够了。但我们通常也不知道那些文件占用的空间大&#xff0c;这时候…

Linux 内核态,用户态,以及如何从内核态到用户态,交互方式有哪些

一、Linux 内核态&#xff0c;用户态 Linux 内核态&#xff0c;用户态&#xff0c;以及如何从内核态到用户态&#xff0c;我来说下我的理解 很多面试官&#xff0c;面试也是照搬照套&#xff0c;网上找的八股文面试题&#xff0c;面试的人也是背八股文&#xff0c;刚好背到了&…

linux 虚拟环境下源码安装DeepSpeed

第一步&#xff1a;创建虚拟环境&#xff1a; conda create -n deepspeed python3.10 第二步&#xff1a;进入虚拟环境&#xff0c;安装Pytorch 2.3.1 # CUDA 12.1 conda install pytorch2.3.1 torchvision0.18.1 torchaudio2.3.1 pytorch-cuda12.1 -c pytorch -c nvidia 第…

OJ题:随机链表的复制—Java数据结构

目录 随机链表的复制 1. 完整题目 2.错误做法 3.第一次遍历 1.拷贝所有旧节点的val域 2. 串联老节点和新节点 3. 第一次遍历代码&#xff1a; 4.第二次遍历 1. 表示出新链表的节点 2. 表示出新节点的next,random 3. 通过映射关系赋值next,random 4. 第二次遍历代码…