【Redis入门到精通十一】Redis集群

news2024/12/23 17:46:02

目录

集群

1.三种分片算法

1.哈希求余算法

2.一致性哈希算法

3.哈希槽分区算法

2.搭建集群环境

3.集群故障处理

4.集群扩容


集群

上篇文章我们了解Redis哨兵的相关操作,使用哨兵只是解决了主节点瘫痪,从节点不能自动变为主节点的问题,并没有解决“单点问题”,(写操作只能由主节点完成)。 为了能存储更多的数据,分担主从节点的读/写压力,于是Redis引入了集群的相关操作, 引⼊多组 Master / Slave ,每⼀组 Master / Slave 存储数据全集的 ⼀部分,从⽽构成⼀个更⼤的整体,称为 Redis 集群 (Cluster)。

1.三种分片算法

如上图所示,在把全集数据平均分成多个部分,交给多个Redis主从服务器存储数据,这就是Redis集群,其中每个master与其对应的slave保存的是同样的数据,占总数据的1/3。

每个 Slave 都是对应 Master 的备份(当 Master 挂了, 对应的 Slave 会补位成 Master)。每个Redis主节点与其对应的从节点称为是⼀个 分⽚ (Sharding)。如果全量数据进⼀步增加, 只要再增加更多的分⽚, 即可解决。我们需要把全量数据尽可能的平均分给每个分片,以免造成Redis服务器压力不均衡,这个时候就需要用到分片算法了。Redis在设计时使用的便是哈希槽分区算法。
1.哈希求余算法
设有 N 个分⽚, 使⽤ [0, N-1] 这样序号进行编号。
针对某个给定的 key, 先计算 hash 值, 再把得到的结果 % N, 得到的结果即为分⽚编号。
例如, N 为 3. 给定 key 为 hello, 对 hello 计算 hash 值(⽐如使⽤ md5 算法), 得到的结果为bc4b2a76b9719d91 , 再把这个结果 % 3, 结果为 0, 那么就把 hello 这个 key 放到 0 号分片上。

后续如果要取某个 key 的记录, 也是针对 key 计算 hash , 再对 N 求余, 就可以找到对应的分⽚编号了。

优点: 简单高效, 数据分配均匀。
缺点: ⼀旦需要进行扩容, N 改变了, 原有的映射规则被破坏, 就需要让节点之间的数据相互传输, 重新排列, 以满足新的映射规则。此时需要搬运的数据量是比较多的, 开销较⼤。
2.一致性哈希算法
为了降低上述的搬运开销, 能够更⾼效扩容, 业界提出了 "⼀致性哈希算法"。
key 映射到分⽚序号的过程不再是简单求余了, ⽽是改成以下过程:
  • 第⼀步, 把 0 -> 2^32-1 这个数据空间, 映射到⼀个圆环上. 数据按照顺时针方向增⻓.
  • 第⼆步, 假设当前存在三个分⽚, 就把分⽚放到圆环的某个位置上.
  • 第三步, 假定有⼀个 key, 计算得到 hash 值 H, 那么这个 key 映射到哪个分⽚呢? 规则很简单, 就是从 H所在位置, 顺时针往下找, 找到的第⼀个分⽚, 即为该 key 所从属的分片.

这就相当于, N 个分⽚的位置, 把整个圆环分成了 N 个管辖区间. Key 的 hash 值落在某个区间内, 就归对应区间管理.

 在这种情况下,扩容只需要原有分片在环上的位置不动, 只要在环上新安排⼀个分⽚位置即可。

优点: 大大降低了扩容时数据搬运的规模, 提⾼了扩容操作的效率。
缺点: 数据分配不均匀 (有的多有的少, 数据倾斜)。
3.哈希槽分区算法
为了解决上述问题 (搬运成本⾼ 和 数据分配不均匀), Redis cluster 引⼊了哈希槽 (hash slots) 算法。
hash_slot = crc16(key) % 16384

crc16是一种哈希算法,16384其实就是2^14。

相当于是把整个哈希值, 映射到 16384 个槽位上, 也就是 [0, 16383].
然后再把这些槽位⽐较均匀的分配给每个分⽚. 每个分⽚的节点都需要记录⾃⼰持有哪些分⽚.
假设当前有三个分⽚, ⼀种可能的分配方式:
  1. 0 号分⽚: [0, 5461], 共 5462 个槽位
  2. 1 号分⽚: [5462, 10923], 共 5462 个槽位
  3. 2 号分⽚: [10924, 16383], 共 5460 个槽位
这⾥的分⽚规则是很灵活的. 每个分⽚持有的槽位也不⼀定连续.
每个分⽚的节点使⽤ 位图 来表⽰⾃⼰持有哪些槽位. 对于 16384 个槽位来说, 需要 2048 个字节(2KB) 大小的内存空间表⽰.

 如果需要进⾏扩容, ⽐如新增⼀个 3 号分⽚, 就可以针对原有的槽位进⾏重新分配。

比如可以把之前每个分⽚持有的槽位, 各拿出⼀点, 分给新分片。

⼀种可能的分配方式:

  1. 0 号分⽚: [0, 4095], 共 4096 个槽位
  2. 1 号分⽚: [5462, 9557], 共 4096 个槽位
  3. 2 号分⽚: [10924, 15019], 共 4096 个槽位
  4. 3 号分⽚: [4096, 5461] + [9558, 10923] + [15019, 16383], 共 4096 个槽位

2.搭建集群环境

接下来给大家教一种基于docker搭建集群的方法,只需要一台云服务器,每个节点都是一个容器。

具体步骤分为以下四步:

1.创建目录和配置

创建 redis-cluster ⽬录. 内部创建两个⽂件

redis-cluster/
├── docker-compose.yml
└── generate.sh

 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
# 注意 cluster-announce-ip 的值有变化. 
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

 执⾏命令

bash generate.sh
配置说明:
  • cluster-enabled yes 开启集群.
  • cluster-config-file nodes.conf 集群节点⽣成的配置.
  • cluster-node-timeout 5000 节点失联的超时时间.
  • cluster-announce-ip 172.30.0.101 节点⾃⾝ ip.
  • cluster-announce-port 6379 节点⾃⾝的业务端⼝.
  • cluster-announce-bus-port 16379 节点⾃⾝的总线端⼝. 集群管理的信息交互是通过这个端⼝进⾏的.

2.编写docker-compose.yml

  • 先创建 networks, 并分配⽹段为 172.30.0.0/24
  • 配置每个节点. 注意配置⽂件映射, 端⼝映射, 以及容器的 ip 地址. 设定成固定 ip ⽅便后续的观察和操作.
此处的端⼝映射不配置也可以, 配置的⽬的是为了可以通过宿主机 ip + 映射的端⼝进⾏访问. 通过 容器⾃⾝ ip:6379 的⽅式也可以访问。
version: '3.7'
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

3.启动容器

 使用docker-compose up -d命令启动容器。

4.构建集群

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 表⽰每个主节点需要两个从节点备份

 执⾏之后, 容器之间会进⾏加⼊集群操作.

此时, 使⽤客⼾端连上集群中的任何⼀个节点, 都相当于连上了整个集群.
  • 客⼾端后⾯要加上 -c 选项, 否则如果 key 没有落到当前节点上, 是不能操作的. -c 会⾃动把请求重定向到对应节点.
  • 使⽤ cluster nodes 可以查看到整个集群的情况。

3.集群故障处理

故障判定

集群中的所有节点, 都会周期性的使⽤⼼跳包进⾏通信。

  1. 节点 A 给 节点 B 发送 ping 包, B 就会给 A 返回⼀个 pong 包. ping 和 pong 除了 message type属性之外, 其他部分都是⼀样的. 这⾥包含了集群的配置信息(该节点的id, 该节点从属于哪个分⽚,是主节点还是从节点, 从属于谁, 持有哪些 slots 的位图...).
  2. 每个节点, 每秒钟, 都会给⼀些随机的节点发起 ping 包, 而不是全发⼀遍. 这样设定是为了避免在节点很多的时候, ⼼跳包也⾮常多(⽐如有 9 个节点, 如果全发, 就是 9 * 8 有 72 组⼼跳了, 而且这是按照 N^2 这样的级别增⻓的).
  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 就彻底被判定为故障节点了.

某个或者某些节点宕机, 有的时候会引起整个集群都宕机 (称为 fail 状态).
以下三种情况会出现集群宕机:
  1. 某个分⽚, 所有的主节点和从节点都挂了.
  2. 某个分⽚, 主节点挂了, 但是没有从节点.
  3. 超过半数的 master 节点都挂了.

 故障迁移

一个节点A发生了故障,如果A是从节点不会发生故障迁移,但是如果A是主节点,那么便会触发故障迁移,所谓故障迁移,就是指把从节点提拔成主节点,继续给整个 redis 集群提供⽀持。

具体流程如下:
  1. 从节点判定⾃⼰是否具有参选资格. 如果从节点和主节点已经太久没通信(此时认为从节点的数据和主节点差异太⼤了), 时间超过阈值, 就失去竞选资格.
  2. 具有资格的节点, ⽐如 C 和 D, 就会先休眠⼀定时间. 休眠时间 = 500ms 基础时间 + [0, 500ms] 随机时间 + 排名 * 1000ms. offset 的值越⼤, 则排名越靠前(越⼩).
  3. 比如 C 的休眠时间到了, C 就会给其他所有集群中的节点, 进⾏拉票操作. 但是只有主节点才有投票资格.
  4. 主节点就会把⾃⼰的票投给 C (每个主节点只有 1 票). 当 C 收到的票数超过主节点数⽬的⼀半, C 就会晋升成主节点. (C ⾃⼰负责执⾏ slaveof no one, 并且让 D 执⾏ slaveof C).
  5. 同时, C 还会把⾃⼰成为主节点的消息, 同步给其他集群的节点. ⼤家也都会更新⾃⼰保存的集群结构信息.

 上述选举过程被称为Raft算法,是一种在分布式系统中广泛使用的算法。在随机休眠时间的加持下, 基本上就是谁先唤醒, 谁就能竞选成功。

4.集群扩容

扩容是⼀个在开发中⽐较常遇到的场景。
随着业务的发展, 现有集群很可能⽆法容纳⽇益增⻓的数据. 此时给集群中加⼊更多新的机器, 就可以使存储的空间更⼤了。

 所谓分布式的本质, 就是使⽤更多的机器, 引⼊更多的硬件资源。

 集群扩容我们可以分为三个步骤:

第一步:把新的主节点加⼊到集群

redis-cli --cluster add-node 172.30.0.110:6379 172.30.0.101:6379
第⼆步: 重新分配 slots
redis-cli --cluster reshard 172.30.0.101:6379
执⾏之后, 会进⼊交互式操作, redis 会提⽰⽤⼾输⼊以下内容:
  • 多少个 slots 要进⾏ reshard ? (此处我们填写 4096)
  • 哪个节点来接收这些 slots ? (此处我们填写 172.30.0.110 这个节点的集群节点 id)
  • 这些 slots 从哪些节点搬运过来? (此处我们填写 all, 表示从其他所有的节点都进行搬运)

第三步: 给新的主节点添加从节点

光有主节点了, 此时扩容的⽬标已经初步达成. 但是为了保证集群可⽤性, 还需要给这个新的主节点添加从节点, 保证该主节点宕机之后, 有从节点能够顶上。
redis-cli --cluster add-node 172.30.0.111:6379 172.30.0.101:6379 --clusterslave --cluster-master-id [172.30.1.110 节点的 nodeId]

❤️😍😍😍😍😍😍😍😍😍😍😍😍😍😍😍😍😍

🍔我是小皮侠,谢谢大家都能看到这里!!

🦚主页已更新Java基础内容,数据结构基础,数据库,算法,Redis相关内容。

🚕未来会更新Java项目,SpringBoot,docker,mq,微服务以及各种Java路线会用到的技术。

🎃求点赞!求收藏!求评论!求关注!

🤷‍♀️谢谢大家!!!!!!!!

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

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

相关文章

[ComfyUI]太赞了!阿里妈妈发布升级版 Flux 图像修复模型,更强细节生成,更高融合度以及更大分辨率支持

小伙伴们还记得我们之前介绍的阿里妈妈发布的 Flux 的 ControlNet 图像修复模型不&#xff0c;之前发布的是 Alpha 早期测试版本&#xff0c;说实话和 Flux 原生的重绘其实差距不大&#xff0c;有些方面甚至还是原生的效果更好。 但是现在&#xff0c;Alpha 的升级版本 Beta 版…

基于java的零食销售系统(源码+定制+开发)

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

qt小练习

制作简易闹钟 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimer> //定时器类 #include <QDebug> //信息调试类 #include <QMessageBox> //消息对话框类 #include <QTime> //时间类 #include…

【C语言复习】常见概念(零基础)

【C语言复习】常见概念 1、C语言是什么&#xff1f;2、C语言的历史和辉煌3、 编译器的选择VS20223.1编译和链接3.2编译器的对比3.3 VS2022 的优缺点 4、VS项⽬ 和 源⽂件、头⽂件介绍5、第一个C语言程序6、main函数&#xff08;主函数&#xff09;7、printf和库函数8、关键字介…

基于springboot的大学生体质测试管理系统(含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于springboot的大学生体质测试管理系统1拥有三种角色 管理员&#xff1a;学生管理、教师管理、日常运行管理、运动分析管理、成绩管理、论坛管理、轮播图管理等 教师&#xff1a;登录…

如何在RuoYi-Vue项目中去除`/dev-api`前缀

前言 在使用RuoYi-Vue框架进行Web应用开发时&#xff0c;有时会遇到API路径需要特定前缀的问题。例如&#xff0c;在某些情况下&#xff0c;开发者可能希望移除或更改默认的/dev-api前缀。 问题描述 当使用YApi直接请求后台接口时&#xff0c;无需添加/dev-api前缀。在生成和…

C++第十六节课 万字详细手动实现string类!

std::basic_string std::basic_string 是 C 标准库中定义的一个模板类&#xff0c;它用于表示字符串。C 中的 std::string 实际上是 std::basic_string<char> 的一个特化版本。也就是说&#xff0c;std::string 是 std::basic_string 这个模板类的一个具体实现&#xff0…

DAY29|| 93.复原ip地址 |78.子集 |90.子集Ⅱ

93.复原ip地址 题目&#xff1a;93. 复原 IP 地址 - 力扣&#xff08;LeetCode&#xff09; 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.…

国外电商系统开发-运维系统上传脚本

创建脚本的方式有两种&#xff1a;第一种脚本文件&#xff0c;第二种在线写脚本。并且友好的支持中文的显示和脚本的中文名。 第一种是从您的PC电脑上传一个脚本文件&#xff0c;当然了&#xff0c;还是以老用法&#xff0c;直接拖动就行&#xff1a; 第二种上传方式&#xff0…

『网络游戏』服务器日志工具类优化【18】

创建脚本&#xff1a;PECommon.cs 编写脚本&#xff1a;PECommon.cs 修改脚本&#xff1a;LoginSys 替换 替换完成 修改客户端脚本&#xff1a;ResSvc.cs 本章结束

仓储物流行业--仓储服务升级经典案例

在当今内外贸竞争激烈的仓储物流行业&#xff0c;高效的数据处理和管理是企业提升竞争力的关键。杭州某供应链公司作为一家专注为中小卖家提供定制仓储服务方案的第三方云仓&#xff0c;在业务发展过程中面临着数据处理方面的诸多挑战。本文将详细介绍云仓项目如何通过轻易云数…

上海交通大学震撼发布:首个OpenAI O1项目复现报告,揭秘独家经验!

来源 | 机器之心 团队介绍&#xff1a;本项目的核心开发团队主要由上海交通大学 GAIR 研究组的本科三年级、四年级学生以及直博一年级研究生组成。项目得到了来自 NYU 等一线大型语言模型领域顶尖研究科学家的指导。 详细作者介绍见&#xff1a;https://github.com/GAIR-NLP/…

FireFox简单设置设置

文章目录 一 设置不显示标签页1原来的样子2新的样子3操作方法 二 设置竖直标签页栏1 效果图2 设置方法 三 设置firefox不提醒更新 一 设置不显示标签页 1原来的样子 2新的样子 3操作方法 地址栏输入 about:config搜索icon,双击选项列表中browserchrome.site icons的值&#…

HWS赛题 入门 MIPS Pwn-Mplogin(MIPS_shellcode)

解题所涉知识点&#xff1a; 泄露或修改内存数据&#xff1a; 堆地址&#xff1a;栈地址&#xff1a;栈上数据的连带输出(Stack Leak) && Stack溢出覆盖内存libc地址&#xff1a;BSS段地址&#xff1a; 劫持程序执行流程&#xff1a;[[MIPS_ROP]] 获得shell或flag&am…

关于css文字下划线动画实现

直接上代码 html部分 <div><span class"txt">文字下滑动画</span></div>css部分 .txt {background: linear-gradient(270deg, #4f95fd 0%, #1059df 100%) no-repeat left bottom;background-size: 0px 2px;background-position-x: right;tr…

汽车微控制器 (MCU)市场报告:未来几年年复合增长率CAGR为5.8%

汽车微控制器是一种高度集成的电路芯片&#xff0c;集成了中央处理器&#xff08;CPU&#xff09;、存储器&#xff08;ROM、RAM、EEPROM等&#xff09;和各种输入输出接口&#xff08;I/O&#xff09;&#xff0c;能够通过软件编程实现对汽车各种电子设备的控制和管理。在汽车…

字符设备驱动模块 dev和misc

字符设备驱动模块 用户空间和内核空间数据拷贝&#xff1a; copy_from_user(); 用户空间数据传给内核 copy_to_user(); 内核数据传给用户空间 动态和静态分别编译 设置Kconfig属性 编译内核文件&#xff08;静态、动态内核文件&#xff09; 启动动态驱动模块 查看动态驱动…

深度学习每周学习总结J2(ResNet-50v2算法实战与解析 - 鸟类识别)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 目录 0. 总结1. 设置GPU2. 导入数据及处理部分3. 划分数据集4. 模型构建部分5. 设置超参数&#xff1a;定义损失函数&#xff0c;学习率&a…

Web开发:总结常见的批处理脚本(.bat)

一、一键复制多个文件 echo off setlocalset source01.pngcopy "%source%" "a.png" copy "%source%" "b.png" copy "%source%" "c.png"endlocal说明&#xff1a; 将上述代码复制到一个新的文本文件中。将文件保…

4.STM32-中断

STM32-中断 需求&#xff1a;红灯每两秒进行闪烁&#xff0c;按键key1控制绿灯亮灭 简单的程序代码无法满足要求 如何让STM32既能执行HAL_DELAY这种耗时的任务&#xff0c;同时又能快速响应按键按下这种突发情况呢 设置中断步骤 1.接入中断 将KEY1输入模式由原先的GPIO_In…