Pytorch分布式训练,数据并行,单机多卡,多机多卡

分布式训练

所有代码可以见我github 仓库:https://github.com/xiejialong/ddp_learning.git

数据并行(Data Parallelism,DP)

跨多个gpu训练模型的最简单方法是使用 torch.nn.DataParallel. 在这种方法中,模型被复制到所有可用的GPU上,并且所有进程都由第一个GPU(也称为主进程)管理。该方法将输入拆分到gpu上,并行计算梯度,并在主进程上更新模型参数之前对它们进行平均。更新后,主进程将更新后的参数广播给所有其他gpu。

DataParallel并不推荐,有以下原因:

  • 额外开支较大:虽然它很容易使用,但它有一些通信开销,因为要等待所有gpu完成反向传播、收集梯度并广播更新的参数。为了获得更好的性能,特别是在扩展到多个节点时,请使用分布式数据并行DistributedDataParallel(DDP)
  • 显存占用大:主GPU的内存使用率比其他GPU高,因为它收集了其他GPU的所有梯度。因此,如果您在单个GPU上已经存在内存问题,那么dataparlil将使其变得更糟。

注意,dataparllel在反向传播后平均gpu之间的梯度。确保相应地缩放学习率(乘以gpu的数量)以保持相同的有效学习率。这同样适用于批处理大小,提供给数据加载器的批处理大小在gpu上进行划分<

例子:

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset
import osclass MyModel(nn.Module): # 模型定义def __init__(self):super().__init__()self.net = nn.Sequential(nn.Linear(10, 10000), nn.Linear(10000, 5000),nn.Linear(5000, 2))def forward(self, x):return self.net(x)class MyData(Dataset): # 数据集定义def __init__(self):super().__init__()self.data_x = torch.concat([torch.rand(size=(10000, 10)) + torch.zeros(size=(10000, 10)), torch.rand(size=(10000, 10)) + torch.ones(size=(10000, 10))], dim=0)self.data_y = torch.concat([torch.zeros(size=(10000, ), dtype=torch.long), torch.ones(size=(10000, ), dtype=torch.long)], dim=0)def __getitem__(self, index):x = self.data_x[index]y = self.data_y[index]return x, ydef __len__(self):return len(self.data_x)train_data = MyData()  # 实例化数据集
train_loader = DataLoader(dataset=train_data, batch_size=64, shuffle=True)
model = MyModel() # 实例化模型
if torch.cuda.device_count() > 1:model = nn.DataParallel(model) 
model = model.cuda()optimizer = optim.Adam(model.parameters(), lr=0.0001) # 定义优化器
criterion = nn.CrossEntropyLoss() # 定义评价器
print(len(train_loader))
for data, target in train_loader:data, target = data.cuda(), target.cuda() # 数据放入显卡optimizer.zero_grad() # 梯度归零output = model(data) # 模型推理loss = criterion(output, target) # 计算lossloss.backward() # 反向传播梯度optimizer.step() # 模型参数更新print(loss.item())
分布式数据并行(Distributed Data Parallelism, DDP)

为了获得更好的性能,PyTorch提供了torch.nn.parallel.distributedDataParallel(DDP),它对于多gpu训练更有效,特别是对于多节点设置。事实上,当使用DDP时,训练代码分别在每个GPU上执行,每个GPU直接与其他GPU通信,并且仅在必要时进行通信,从而减少了通信开销。在DDP方法中,主进程的作用大大减少,每个GPU负责自己的向前和向后传递,以及参数更新。向前传递后,开始向后传递,每个GPU开始将自己的梯度发送给所有其他GPU,每个GPU接收所有其他GPU的梯度之和。这个过程被称为all-reduce操作。之后,每个GPU都有完全相同的梯度,并更新其自己的模型副本的参数。Reduce:分布式计算中的一种常见操作,其中计算结果跨多个进程聚合。All -reduce意味着所有进程都调用Reduce操作来接收来自所有其他进程的结果。

基于torch.multiprocessing的启动方式

启动程序时不需要在命令行输入额外的参数,写起来也比较容易,但是调试较麻烦

import os
import torch
import torch.distributed as dist  # 分布式库
import torch.multiprocessing as mp  # 多线程
from torch.utils.data import Dataset, DataLoader, DistributedSampler  # 数据集库
import torch.nn as nn  # 网络结构库
import torch.optim as optim  # 优化器库
from torch.amp import autocast, GradScaler  # 混合精度库os.environ["CUDA_VISIBLE_DEVICES"]='2,3'scaler = GradScaler() # 自动缩放梯度class MyModel(nn.Module): # 模型定义def __init__(self):super().__init__()self.net = nn.Sequential(nn.Linear(10, 10000), nn.Linear(10000, 5000),nn.Linear(5000, 2))def forward(self, x):return self.net(x)class MyData(Dataset): # 数据集定义def __init__(self):super().__init__()self.data_x = torch.concat([torch.rand(size=(10000, 10)) + torch.zeros(size=(10000, 10)), torch.rand(size=(10000, 10)) + torch.ones(size=(10000, 10))], dim=0)self.data_y = torch.concat([torch.zeros(size=(10000, ), dtype=torch.long), torch.ones(size=(10000, ), dtype=torch.long)], dim=0)def __getitem__(self, index):x = self.data_x[index]y = self.data_y[index]return x, ydef __len__(self):return len(self.data_x)def worker(rank, world_size):dist.init_process_group("nccl", rank=rank, world_size=world_size) # 定义通信方式torch.cuda.set_device(rank) # 设置当前线程控制的GPUprint("init model")model = MyModel().cuda()print(f"init ddp rank {rank}")ddp_model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[rank]) traindata = MyData()train_sampler = DistributedSampler(dataset=traindata, shuffle=True) # 定义分布式数据采集器train_loader = DataLoader(traindata, batch_size=64,sampler=train_sampler, num_workers=4, pin_memory=True) # 定义数据加载器optimizer = optim.Adam(ddp_model.parameters(), lr=0.0001) # 定义优化器criterion = nn.CrossEntropyLoss() # 定义评价函数print("train")accumulation_steps = 4 # 设置梯度累计次数optimizer.zero_grad(set_to_none=True) # 重设梯度for batch_idx, (inp, target) in enumerate(train_loader):inp, target = inp.cuda(), target.cuda()with autocast(device_type="cuda"): # 开启混合精度训练output = ddp_model(inp)loss = criterion(output, target)loss = loss / accumulation_steps  # 归一化损失scaler.scale(loss).backward() # 混合精度训练下进行损失缩放并执行后向传播if (batch_idx + 1) % accumulation_steps == 0:# optimizer.step()  # 更新权重scaler.step(optimizer) # 混合精度下的权重更新scaler.update()optimizer.zero_grad(set_to_none=True)  # 每次更新完进行梯度清零print(loss)dist.barrier()dist.destroy_process_group()if __name__ == "__main__":world_size = torch.cuda.device_count()mp.spawn(worker, nprocs=world_size, args=(world_size,))

启动多GPU训练的命令(高版本被移除rank):

python -m torch.distributed.launch --nproc_per_node=4 train.py

或使用torchrun

torchrun --nproc_per_node=4 train.py

基于torch.distributed的启动方式

一个完整的训练架构

参考:

https://blog.csdn.net/wxc971231/article/details/132827787

import os
import torch
import torch.nn as nn
import torch.optim as optim
import torch.distributed as dist
from torch.utils.data import Dataset, DataLoader, DistributedSampler
import argparse
import numpy as np
from tqdm import tqdm
os.environ["CUDA_VISIBLE_DEVICES"]="2,3"class MyModel(nn.Module): # 模型定义def __init__(self):super().__init__()self.net = nn.Sequential(nn.Linear(10, 10000), nn.Linear(10000, 5000),nn.Linear(5000, 2))def forward(self, x):return self.net(x)class MyData(Dataset): # 数据集定义def __init__(self):super().__init__()self.data_x = torch.concat([torch.rand(size=(10000, 10)) + torch.zeros(size=(10000, 10)), torch.rand(size=(10000, 10)) + torch.ones(size=(10000, 10))], dim=0)self.data_y = torch.concat([torch.zeros(size=(10000, ), dtype=torch.long), torch.ones(size=(10000, ), dtype=torch.long)], dim=0)def __getitem__(self, index):x = self.data_x[index]y = self.data_y[index]return x, ydef __len__(self):return len(self.data_x)def load_train_objs(ags):train_dataset = MyData() # 定义数据集train_sampler = DistributedSampler(train_dataset, num_replicas=ags.world_size, rank=ags.rank, shuffle=True)  # 将数据集进行均分train_loader = DataLoader(train_dataset, batch_size=args.batch_size, sampler=train_sampler, pin_memory=True) # 定义数据加载器model = MyModel() # 定义模型model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model)model.to(ags.device)ddp_model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[ags.local_rank]) # 把模型放入不同的gpureturn train_loader, ddp_modeldef init_ddp_env(args):# 分布式同行环境初始化dist.init_process_group(backend='nccl', init_method="env://")# 获取全局/本地 rank、world_sizeargs.rank = int(os.environ.get("RANK", -1))args.local_rank = int(os.environ.get("LOCAL_RANK", -1))args.world_size = int(os.environ.get("WORLD_SIZE", -1))# 设置GPU显卡绑定torch.cuda.set_device(args.local_rank)args.device = torch.device("cuda")# 打印绑定信息print(f"[RANK {args.rank} | LOCAL_RANK {args.local_rank}] Using CUDA device {torch.cuda.current_device()}: {torch.cuda.get_device_name(torch.cuda.current_device())} | World size: {args.world_size}")dist.barrier() # 等待所有进程都初始化完毕,即所有GPU都要运行到这一步以后再继续class Trainer:def __init__(self, args, model: torch.nn.Module, train_loader: DataLoader, optimizer: torch.optim.Optimizer, criterion):self.model = modelself.train_loader = train_loaderself.optimizer = optimizerself.criterion = criterionself.device = args.deviceself.snapshot_path = args.snapshot_pathself.gpu_id = args.local_rankself.max_epochs = args.max_epochsself.save_every = args.save_everyself.epochs_run = 0if os.path.exists(args.resume_path):print('loading snapshot')self._load_snapshot(args.resume_path)def _load_snapshot(self, resume_path):''' 加载 snapshot 并重启训练 '''loc = f"cuda:{self.gpu_id}"snapshot = torch.load(resume_path, map_location=loc)self.model.load_state_dict(snapshot["MODEL_STATE"])self.epochs_run = snapshot["EPOCHS_RUN"]print(f"Resuming training from snapshot at Epoch {self.epochs_run}")def _run_batch(self, inp, targets):self.optimizer.zero_grad()output = self.model(inp)loss = self.criterion(output, targets)loss.backward()self.optimizer.step()return loss.item()def _run_epoch(self, epoch):epoch_losses = []self.train_loader.sampler.set_epoch(epoch)            # 设置 epoch 保证多 GPU 上数据不重叠for inp, targets in self.train_loader:inp = inp.to(self.device)targets = targets.to(self.device)loss = self._run_batch(inp, targets)epoch_losses.append(loss)return np.mean(epoch_losses)def _save_snapshot(self, epoch):# 在 snapshot 中保存恢复训练所必须的参数snapshot = {"MODEL_STATE": self.model.state_dict(),  # 由于多了一层 DDP 包装,通过 .module 获取原始参数 "EPOCHS_RUN": epoch,}save_path = os.path.join(self.snapshot_path, f"epoch_{epoch}.pt")torch.save(snapshot, save_path)# print(f"Epoch {epoch} | Training snapshot saved at {save_path}")def train(self):# 现在从 self.epochs_run 开始训练,统一重启的情况with tqdm(total=self.max_epochs, desc=f"[GPU{self.gpu_id}] Training", position=self.gpu_id, initial=self.epochs_run) as pbar:for epoch in range(self.epochs_run, self.max_epochs + self.epochs_run):epoch_loss = self._run_epoch(epoch)                         # 各个 GPU 上都在跑一样的训练进程,这里指定 rank0 进程保存 snapshot 以免重复保存if self.gpu_id == 0 and epoch % self.save_every == 0:self._save_snapshot(epoch)pbar.set_postfix({'epoch': epoch, 'loss':'{:.2f}'.format(epoch_loss)})pbar.update()def worker(args):init_ddp_env(args)  # 初始化分布式环境train_loader, ddp_model = load_train_objs(args)  # 导入分布式数据导入器和模型optimizer = optim.Adam(ddp_model.parameters(), lr=args.lr)criterion = nn.CrossEntropyLoss()trainer = Trainer(args, ddp_model, train_loader, optimizer, criterion)trainer.train()if __name__=="__main__":import argparseparser = argparse.ArgumentParser(description='simple distributed training job')parser.add_argument('--rank', default=-1, type=int, help='Rank (default: -1)')parser.add_argument('--world_size', default=1, type=int, help='world_size (default: -1)')parser.add_argument('--local_rank', default=-1, type=int, help='local_rank (default: 1)')parser.add_argument('--device', default="cuda", type=str, help='local_rank (default: 1)')parser.add_argument('--batch_size', default=32, type=int, help='Input batch size on each device (default: 32)')parser.add_argument('--lr', default=0.001, type=float, help='Learing rate (default: 0.001)')parser.add_argument('--snapshot_path', default="checkpoints/", type=str, help='Path of checkpoints (default: checkpoints/)')parser.add_argument('--save_every', default=1, type=int, help='Frequence of checkpoint save')parser.add_argument('--max_epochs', default=5, type=int, help='Total epoch')parser.add_argument('--resume_path', default="checkpoints/epoch_2.pt", type=str, help='Path of resume file')args = parser.parse_args()worker(args)#  torchrun --nnodes=2 --nproc_per_node=2 --node_rank=0 --master_addr=xxx --master_port=xx xxx.py
# --nnodes: 表示参与训练的总机器数
# --nproc_per_node:表示每台机器上要启动几个训练进程,一个进程对应一个 GPU,因通常设置为你机器上要用到的GPU数。整个分布式环境下,总训练进程数 = nnodes * nproc_per_node
# --node_rank:表示当前机器是第几台机器,从 0 开始编号,必须每台机器都不同
# --master_addr 和 --master_port:指定主节点的 IP 和端口,用于 rendezvous(进程对齐)和通信初始化,所有机器必须填写相同的值!
多机多卡的启动和bash

参考:https://cloud.tencent.com/developer/article/2514642

下面是一个简单的分布式训练代码

import os
from time import sleep
import torch
import torch.distributed as dist
import torch.nn as nn
import torch.optim as optim
import datetimefrom torch.nn.parallel import DistributedDataParallel as DDPclass ToyModel(nn.Module):def __init__(self):super(ToyModel, self).__init__()self.net1 = nn.Linear(10, 10)self.relu = nn.ReLU()self.net2 = nn.Linear(10, 5)def forward(self, x):return self.net2(self.relu(self.net1(x)))def train():local_rank = int(os.environ["LOCAL_RANK"])rank = int(os.environ["RANK"])while True:print(f"[{os.getpid()}] (rank = {rank}, local_rank = {local_rank}) training...")model = ToyModel().cuda(local_rank)ddp_model = DDP(model, [local_rank])loss_fn = nn.MSELoss()optimizer = optim.SGD(ddp_model.parameters(), lr=0.001)optimizer.zero_grad()outputs = ddp_model(torch.randn(20, 10).to(local_rank))labels = torch.randn(20, 5).to(local_rank)loss = loss_fn(outputs, labels)loss.backward()print(f"[{os.getpid()}] (rank = {rank}, local_rank = {local_rank}) loss = {loss.item()}\n")optimizer.step()sleep(1)def run():env_dict = {key: os.environ[key]for key in ("MASTER_ADDR", "MASTER_PORT", "WORLD_SIZE", "LOCAL_WORLD_SIZE")}print(f"[{os.getpid()}] Initializing process group with: {env_dict}")dist.init_process_group(backend="nccl", timeout=datetime.timedelta(seconds=30))train()dist.destroy_process_group()if __name__ == "__main__":run()

在多个主机上执行

torchrun --nproc_per_node=M --nnode=N --node_rank=0 --master_addr='xxx.xxx.xxx.xxx' --master_port=1234 ddp_multi_master.py

注意这里参数M表示你单个机器上的显卡数,N是你有几台机器,--node_rank,这里是不同机器上的区别,主机上设置0,其他机器上设置1,2,…,N-1.

也可以写bash文件执行更方便

#!/bin/bash
# 设置基本参数
MASTER_ADDR=xxx.xxx.xxx.xxx           # 主机IP
MASTER_PORT=29400                   # 主机端口
NNODES=2                            # 参与训练的总机器数
NPROC_PER_NODE=2                    # 每台机器上的进程数# 所有网卡的IP地址,用于筛选
ALL_LOCAL_IPS=$(hostname -I)
# 根据本机 IP 配置通信接口
if [[ "$ALL_LOCAL_IPS" == *"xxx.xxx.xxx.xxx"* ]]; thenNODE_RANK=0                       # 表示当前机器是第0台机器IFNAME=eno1   # 机器0的网卡名称mytorchrun=~/anaconda3/envs/lora/bin/torchrun  # 虚拟环境下torchrun的位置
elif [[ "$ALL_LOCAL_IPS" == *"xxx.xxx.xxx.xxx"* ]]; thenNODE_RANK=1                       # 表示当前机器是第1台机器IFNAME=enp6s0 # 机器1的网卡名称mytorchrun=/home/users1/xjl/miniconda3/envs/lora/bin/torchrun
elseexit 1
fi# 设置 RDMA 接口
export NCCL_IB_DISABLE=0            # 是否禁用InfiniBand
export NCCL_IB_HCA=mlx5_1           # 使用哪个RDMA接口进行通信
export NCCL_SOCKET_IFNAME=$IFNAME   # 使用哪个网卡进行通信
export NCCL_DEBUG=INFO              # 可选:调试用export GLOO_IB_DISABLE=0            # 是否禁用InfiniBand
export GLOO_SOCKET_IFNAME=$IFNAME   # 使用哪个网卡进行通信
export PYTHONUNBUFFERED=1           # 实时输出日志# 启动分布式任务
$mytorchrun \--nnodes=$NNODES \--nproc_per_node=$NPROC_PER_NODE \--node_rank=$NODE_RANK \--master_addr=$MASTER_ADDR \--master_port=$MASTER_PORT \ddp_multi_master.py

主机输出信息:
在这里插入图片描述
从机输出信息:
在这里插入图片描述

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

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

相关文章

【论文阅读】——D^3-Human: Dynamic Disentangled Digital Human from Monocular Vi

文章目录 摘要1 引言2 相关工作3 方法3.1 HmSDF 表示3.2 区域聚合3.3. 变形场3.4. 遮挡感知可微分渲染3.5 训练3.5.1 训练策略3.5.2 重建损失3.5.3 正则化限制 4. 实验4.1 定量评估4.2 定性评价4.3 消融研究4.4 应用程序 5 结论 摘要 我们介绍 D 3 D^{3} D3人&#xff0c;一种…

docker commit除了提交容器成镜像,还能搞什么之修改cmd命令

要让新镜像默认启动时执行 /usr/sbin/sshd -D&#xff0c;需在提交镜像时 ​​显式指定新的启动命令​​。 方法一&#xff1a;提交时通过 --change 覆盖 CMD docker commit --changeCMD ["/usr/sbin/sshd", "-D"] v2 project:v2 方法二&#xff1a;重…

为什么我输入对了密码,还是不能用 su 切换到 root?

“为什么我输入对了密码&#xff0c;还是不能用 su 切换到 root&#xff1f;” 其实这背后可能不是“密码错了”&#xff0c;而是系统不允许你用 su 切 root&#xff0c;即使密码对了。 &#x1f447; 以下是最常见的几个真正原因&#xff1a; ❌ 1. Root 用户没有设置密码&…

转移dp简单数学数论

1.转移dp问题 昨天的练习赛上有一个很好玩的起终点问题&#xff0c;第一时间给出bfs的写法。 但是写到后面发现不行&#xff0c;还得是的dp转移的写法才能完美的解决这道题目。 每个格子可以经过可以不经过&#xff0c;因此它的状态空间是2^&#xff08;n*m&#xff09;&…

IP查询基础介绍

IP 查询原理 IP 地址是网络设备唯一标识&#xff0c;IP 查询通过解析 IP 地址获取地理位置、运营商等信息。目前主流的 IPv4&#xff08;32 位&#xff09;与 IPv6&#xff08;128 位&#xff09;协议&#xff0c;前者理论提供约 43 亿地址&#xff0c;后者地址空间近乎无限。…

Linux命令简介

1 Linux系统的命令概述 在 Linux 操作系统中&#xff0c;凡是在字符操作界面中输入能够完成特定操作和任务的字符串都可以称为命令。严格来说&#xff0c;命令通常只代表实现某一类功能的指令或程序的名称。 1.1 Shell Linux 命令的执行必须依赖于 Shell 命令解释器。Shell …

WebRTC与RTSP|RTMP的技术对比:低延迟与稳定性如何决定音视频直播的未来

引言 音视频直播技术已经深刻影响了我们的生活方式&#xff0c;尤其是在教育、医疗、安防、娱乐等行业中&#xff0c;音视频技术成为了行业发展的重要推动力。近年来&#xff0c;WebRTC作为一种开源的实时通信技术&#xff0c;成为了音视频领域的重要选择&#xff0c;它使得浏览…

多通道振弦式数据采集仪MCU安装指南

设备介绍 数据采集仪 MCU集传统数据采集器与5G/4G,LoRa/RS485两种通信功能与一体的智能数据采集仪。该产品提供振弦、RS-485等的物理接口&#xff0c;能自动采集并存储多种自然资源、建筑、桥梁、城市管廊、大坝、隧道、水利、气象传感器的实时数据&#xff0c;利用现场采集的数…

Vue3 + Element Plus表格筛选样式设置

如果弹出框挂载在 body 下&#xff08;而非组件内部&#xff09;&#xff0c;scoped 样式无法生效&#xff0c;这时就需要使用全局样式。 强制全局样式 1、添加全局样式文件&#xff08;或在原有的文件中添加以下内容&#xff09; src/assets/global.scss /* 全局强制样式覆…

vue--ofd/pdf预览实现

背景 实现预览ofd/pdf超链接功能 业务实现 pdf的预览 实现方式&#xff1a; 直接使用 <iframe :src"${url}#navpanes0&toolbar0" /> 实现pdf的预览。 navpanes0 隐藏侧边栏toolbar0 隐藏顶部工具栏 使用pdf.js&#xff0c;代码先行&#xff1a; <tem…

【C++20新特性】ranges::sort()使用方法,优势,注意点

以下是关于 ranges::sort() 的详细说明&#xff1a; 1. ranges::sort() 的使用方法 ranges::sort() 是 C20 引入的基于范围&#xff08;Ranges&#xff09;的排序函数&#xff0c;其语法更简洁&#xff0c;支持直接操作容器或范围对象。 (1)基本用法 #include <vector&g…

深入理解设计模式之适配器模式

深入理解设计模式之适配器模式 1. 适配器模式概述 适配器模式(Adapter Pattern)是一种结构型设计模式&#xff0c;它允许将一个类的接口转换为客户端所期望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的类能够协同工作&#xff0c;扮演了"转换器&quo…

【数据结构 · 初阶】- 快速排序

目录 一. Hoare 版本 1. 单趟 2. 整体 3. 时间复杂度 4. 优化&#xff08;抢救一下&#xff09; 4.1 随机选 key 4.2 三数取中 二. 挖坑法 格式优化 三. 前后指针&#xff08;最好&#xff09; 四. 小区间优化 五. 改非递归 快速排序是 Hoare 提出的一种基于二叉树…

第2周 PINN核心技术揭秘: 如何用神经网络求解偏微分方程

1. PDEs与传统数值方法回顾 (Review of PDEs & Traditional Numerical Methods) 1.1 什么是偏微分方程 (Partial Differential Equations, PDEs)? 偏微分方程是描述自然界和工程领域中各种物理现象(如热量传播、流体流动、波的振动、电磁场分布等)的基本数学语言。 1.…

Neo4j(二) - 使用Cypher操作Neo4j

文章目录 前言一、Cypher简介二、数据库操作1. 创建数据库2. 查看数据库3. 删除数据库4. 切换数据库 三、节点、关系及属性操作1. 创建节点与关系1.1 语法1.2 示例 2. 查询数据2.1 语法2.2 示例 3. 更新数据3.1 语法3.2 示例 4. 删除节点与关系4.1 语法4.2 示例 5. 合并数据5.1…

RabbitMQ的Web管理页面给我看懵了,这都什么意思啊

文章目录 OverviewTotalsMessage RatesQueued Messages NodesChurn StatisticsPorts and ContextsExport DefinitionsImport Definitions ConnectionsChannelsExchangesQueuesAdmin他们之间的关联 在上一篇文章中我们讲到了如何在Windows中安装Rabbitmq&#xff0c; 小白也能搞…

安全基础与协议分析

5.1 Web安全基础 5.1.1 Web安全基础概览&#xff08;一、二&#xff09; Web安全的核心目标是保护Web应用及其数据免受攻击&#xff0c;涵盖以下关键领域&#xff1a; 攻击面&#xff1a; 前端漏洞&#xff08;XSS、CSRF&#xff09;。 后端漏洞&#xff08;SQL注入、RCE&a…

STM32项目实战:ADC采集

STM32F103C8T6的ADC配置。PB0对应的是ADC1的通道8。在标准库中&#xff0c;需要初始化ADC&#xff0c;设置通道&#xff0c;时钟&#xff0c;转换模式等。需要配置GPIOB的第0脚为模拟输入模式&#xff0c;然后配置ADC1的通道8&#xff0c;设置转换周期和触发方式。 接下来是I2C…

第十四章:数据治理之数据源:数据源的数据接入、业务属性梳理及监控

本章开始&#xff0c;将进入9大模块的介绍。第一个模块我们先介绍&#xff1a;数据源。数据源是整个数据中台数据的来源&#xff0c;是一个起点。更好的管理好数据源这个起点&#xff0c;是数据治理的一个好的开始。 在【数据&#xff1a;业务生数据&#xff0c;数据生“万物”…

【C/C++】多线程开发:wait、sleep、yield全解析

文章目录 多线程开发&#xff1a;wait、sleep、yield全解析1 What简要介绍详细介绍wait() — 条件等待&#xff08;用于线程同步&#xff09;sleep() — 睡觉&#xff0c;定时挂起yield() — 自愿让出 CPU 2 区别以及建议区别应用场景建议 3 三者协作使用示例 多线程开发&#…