MinIO(看这一篇就够了)

在这里插入图片描述

一.前言

对象存储是一种数据存储架构,设计用于管理和处理大量非结构化数据。与传统的文件存储和块存储不同,对象存储通过将数据分解为离散的、独立的单元或“对象”来存储每个对象包含数据本身、相关的元数据和一个唯一的标识符。

官网:https://www.minio.org.cn

以下是对象存储、服务器磁盘和分布式文件系统的对比表格:

特性对象存储服务器磁盘分布式文件系统
存储方式以对象为基本单位存储数据,每个对象包含数据、元数据和唯一标识符数据直接存储在服务器的本地磁盘上数据分布在多个服务器节点上,通过网络进行数据访问
优点高可扩展性:能够轻松扩展至数十乃至数百EB的容量。 高效性:扁平化结构,不受复杂目录系统对性能的影响。 安全性高:通常凭借HTTP调用对象存储本身提供的认证密钥来提供数据访问。 访问方便:支持HTTP(S)协议,采用REST的API方式调用和检索数据。 成本相对低:与块存储方式相比,对象存储是最具成本效益的数据存储类型。开发便捷:直接使用服务器磁盘,无需复杂的配置和开发工作。 成本低:如果已有服务器和磁盘,不需要额外支付存储服务费用。容易实现扩容:可以通过增加更多的服务器节点来扩展存储容量,实现水平扩展。
缺点最终一致性:由于不同节点的位置不同,数据同步时可能会有一定时间的延迟或者错误。 不适合频繁变动的数据:对象存储比较适合存储那些变动不大甚至不变的文件。扩展困难:服务器磁盘的容量有限,当存储空间不足时,扩展磁盘容量可能需要更换更大容量的硬盘或者增加额外的磁盘阵列,成本较高且操作复杂。复杂度高:分布式文件系统的部署和维护相对复杂,需要专业的运维团队来管理。

分布式文件系统

分布式文件系统(Distributed File System, DFS)是一种文件系统,它使文件可以跨越多个服务器或存储设备存储和访问。DFS 通过网络将多个存储资源组合成一个统一的文件系统,使用户和应用程序可以像访问本地文件一样透明地访问远程文件。

分布式文件系统的关键特性:

  • 透明性:用户和应用程序可以像访问本地文件一样访问远程文件,感受不到底层的复杂性。
  • 高可用性:通过复制和冗余机制,确保即使某些节点或硬件发生故障,数据仍然可用。
  • 可扩展性:能够处理随着数据量和用户数增加而增长的需求。
  • 容错性:通过数据冗余和错误检测机制,保证系统能继续运行,即使发生部分硬件或网络故障。
  • 性能:通过分布式架构,能够有效地处理大量并发访问请求。

MINIO

MinIO 是一个高性能、轻量级对象存储服务器,专为大规模数据存储和分析而设计。它兼容 Amazon S3 API,可以无缝替代 Amazon S3 作为存储后端,并且支持在各种环境中部署,包括物理服务器、虚拟机、容器等。

FastDFS和MINIO的区别

以下是MinIO和FastDFS的对比表格:

特性MinIOFastDFS
存储类型对象存储文件存储
架构单一守护进程,支持分布式Tracker-Storage 分离
协议支持S3 兼容,支持 HTTP/REST API专有协议
数据冗余纠删码,多节点和磁盘故障容错主从复制,多副本
性能高性能,适合大文件和海量数据存储适合小文件存储,上传/下载性能较高
扩展性高度可扩展,支持水平扩展可扩展但管理复杂
管理工具Web 界面,支持 Prometheus 监控命令行工具
生态系统广泛,集成度高生态相对较小,集成度低
安装部署简单,开箱即用复杂,需要专业知识
社区与支持活跃,有官方文档和社区支持缺乏官方文档和持续更新

MinIO和FastDFS各有优势,MinIO在兼容性、扩展性、性能和生态系统方面表现更佳,适合云原生应用、大数据分析等场景;而FastDFS在处理小文件方面性能出色,适合需要海量小文件存储的场景。具体选择可以根据实际需求和使用场景来决定。

二.Windows环境安装Minio

1、下载服务端和客户端安装包文件

下载地址:MinIO | Code and downloads to create high performance object storage
服务端文件:minio.exe 用于接收文件信息
客户端文件:mac.exe 用于上传文件 ,如果用程序代码操作文件存储,只启动服务端就ok

在这里插入图片描述
2、启动minio服务器

特别提示:在windows 安装软件我们都习惯双击.exe 文件启动。minio可不行奥,可不行,可不行。千万不能去双击运行,这样可能会导致最终启动失败;无论是windows还是linux都建议通过命令启动的。

以管理员权限打开cmd窗口,进入到minio.exe所在bin目录
在这里插入图片描述

2.2、设置用户名

用于登录minio客户端

setx MINIO_ROOT_USER name 

设置登录密码

 setx MINIO_ROOT_PASSWORD password

2.3、启动Minio服务

.\minio.exe server D:\develpo\minio\data --console-address "127.0.0.1:9000" --address "127.0.0.1:9005"

D:\develpo\minio\data 指定数据存放路径

9005是控制台端口,9000是服务的端口。
在这里插入图片描述
4.5、访问minio服务器

访问客户端地址 http://127.0.0.1:9000/ 输入用户密码
在这里插入图片描述

使用 Docker 安装

docker run -p 9000:9000 minio/minio server /data

三. 基本概念

基本概念

概念定义特点
Object存储到MinIO的基本对象,如文件、字节流等具有唯一标识,可设置元数据
Bucket用来存储Object的逻辑空间,相当于顶层文件夹数据隔离,命名唯一,可设置权限
Drive存储数据的磁盘,启动时以参数传入是数据存储载体,容量有限
Set一组Drive的集合分布式部署自动划分,对象存储于其上,Drive数量固定,尽可能分布在不同节点

MinIO 纠删码 EC (Erasure Code)

纠删码(Erasure Code, EC) 是一种数据保护方法,它将数据分割成片段,生成冗余数据块,并将这些数据块存储在不同的位置,如磁盘、存储节点或其他地理位置。MinIO 采用 Reed-Solomon 纠删码实现,将对象拆分成数据块和奇偶校验块,以提高数据的冗余性和可用性。

简单来说就是可以通过数学计算,把丢失的数据进行还原,它可以将n份原始数据,增加m份数据,并能通过n+m份中的任意n份数据,还原为原始数据。
即如果有任意小于等于m份的数据失效,仍然能通过剩下的数据还原出来。

举个最简单例子就是有两个数据(d1, d2),用一个校验和y(d1 + d2 = y)即可保证即使丢失其中一个,依然可以还原数据。如丢失 d1 ,则使用 y - d2 = d1 还原,同理,d2 丢失或者y丢失,均可通过计算得出。

存储形式

当文件对象上传到MinIO时,数据会以特定的形式存储在对应的数据存储磁盘中。具体存储形式如下:

  1. 目录结构

    • 以Bucket名称为目录。
    • 文件名称为下一级目录。
    • 文件名下包含编码数据块及检验块(EC码)和元数据文件(xl.meta)。
  2. 数据块和元数据文件

    • 编码数据块及检验块(EC码):这些块存储在奇数编号的磁盘上,例如data01和data03。
    • 元数据文件(xl.meta):这些文件存储在偶数编号的磁盘上,例如data02和data04。

例如,假设我们有四块磁盘:data01、data02、data03、data04。存储形式如下:

  • data01

    • /bucket_name/file_name/part.1(编码数据块)
    • /bucket_name/file_name/part.2(编码数据块)
  • data02

    • /bucket_name/file_name/xl.meta(元数据文件)
  • data03

    • /bucket_name/file_name/part.3(编码数据块)
    • /bucket_name/file_name/part.4(编码数据块)
  • data04

    • /bucket_name/file_name/xl.meta(元数据文件)

这种存储形式确保了数据的冗余和高可用性,通过纠删码技术,即使部分磁盘损坏,数据仍然可以被恢复。

Minio中的存储级别

Minio当前支持两种存储级别:Reduced RedundancyStandard,通过对两种级别的设置来修改对象的Parity Drives ( P)(奇偶校验块)和Data Drives (D)(数据块)的比例,让用户能够更好的控制磁盘使用率和容错性。

STANDARD

STANDARD存储级别包含比REDUCED_REDUNDANCY存储级别更多的奇偶校验块,因此STANDARD存储级别的奇偶校验块需要满足如下条件:

  • 在未设置REDUCED_REDUNDANCY存储级别的情况下,STANDARD存储级别的奇偶校验块需要大于等于2;
  • 在设置了REDUCED_REDUNDANCY存储级别的情况下,STANDARD存储级别的奇偶校验块需要大于REDUCED_REDUNDANCY存储级别的奇偶校验块数量
  • 奇偶校验块的数量必须小于数据块,所以STANDARD存储级别的奇偶校验块不能大于N/2(N为Erasure Set中的磁盘数量)

STANDARD存储级别的奇偶校验块的默认值取决于Erasure Set中的磁盘数量:

Erasure Set SizeDefault Parity (EC:N)
5 or fewerEC:2
6-7EC:3
8 or moreEC:4

REDUCED_REDUNDANCY

REDUCED_REDUNDANCY存储级别包含比STANDARD存储级别更少的奇偶校验块,因此REDUCED_REDUNDANCY存储级别的奇偶校验块需要满足如下条件:

  • 在未设置STANDARD存储级别的情况下,REDUCED_REDUNDANCY存储级别的奇偶校验块需要小于N/2
  • 在设置了STANDARD存储级别的情况下,REDUCED_REDUNDANCY存储级别的奇偶校验块需要小于STANDARD存储级别的奇偶校验块数量
  • 奇偶校验块的数量必须小于数据块,且REDUCED_REDUNDANCY存储级别的奇偶校验块需要小于STANDARD存储级别的奇偶校验块数量,那么REDUCED_REDUNDANCY存储级别的奇偶校验块需要大于等于2,所以Erasure Set中的磁盘数量大于4的时候才支持REDUCED_REDUNDANCY存储级别。

REDUCED_REDUNDANCY存储级别的奇偶校验块默认值为:EC:2

使用

配置存储级别
有以下两种配置方式:

  • 环境变量
export MINIO_STORAGE_CLASS_STANDARD=EC:4
export MINIO_STORAGE_CLASS_RRS=EC:2

设置以上环境变量并重启服务

  • mc admin
mc admin config set myminio/ storage_class standard="EC:4"
mc admin config set myminio/ storage_class rrs="EC:2"

# 重启minio服务
mc admin service restart myminio/

存储方案

在这里插入图片描述

四. 命令操作

列出存储桶

mc ls <alias>

这会列出指定 MinIO 服务器上的所有存储桶。

创建存储桶

mc mb <alias>/<bucket_name>

这会在指定 MinIO 服务器上创建一个新的存储桶。

上传文件

mc cp <file_path> <alias>/<bucket_name>

这会将本地文件上传到指定的 MinIO 存储桶中。

下载文件

mc cp <alias>/<bucket_name>/<file_name> <local_file_path>

这会将 MinIO 存储桶中的文件下载到本地。

复制对象

mc cp <source> <target>

这会复制对象从一个位置到另一个位置,可以是存储桶内的对象或不同存储桶间的对象。

移动对象

mc mv <source> <target>

这会移动对象从一个位置到另一个位置,与复制不同的是,移动后源位置的对象将被删除。

删除对象

mc rm <alias>/<bucket_name>/<object_name>

这会删除指定的对象。

删除存储桶

mc rb <alias>/<bucket_name>

这会删除指定的存储桶及其中的所有对象。

五 . MinIO 整合 SpringBoot

1、导入依赖:

 <dependency>
       <groupId>io.minio</groupId>
       <artifactId>minio</artifactId>
       <version>8.2.2</version>
 </dependency>

2、在SpringBoot的配置文件中编写 MinIO 的配置:

minio:
  config:
    url: http://127.0.0.1:9005 #ip地址
    accessKey: admin #  账号
    secretKey: admin962464 #  密码
    secure: false #如果是true,则用的是https而不是http,默认值是true
    bucketName: "test"  # 桶的名字 相当于文件夹

3、编写 MinIO 配置类:

import io.minio.MinioClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "minio.config")
public class MinioConfig {
    /**
     * 服务地址
     */
    private String url;

    /**
     * 用户名
     */
    private String accessKey;

    /**
     * 密码
     */
    private String secretKey;

    /**
     * 存储桶名称
     */
    private String bucketName;

    @Bean
    public MinioClient getMinioClient() {
        return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build();
    }
}

4、编写 MinIO 的工具类:

import com.jjy.shopping_file_service.config.MinioConfig;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@Slf4j
@Component
public class MinIOUtil {

    @Resource
    private MinioConfig minioConfig;

    @Resource
    private MinioClient minioClient;

    /**
     * 查看存储bucket是否存在
     *
     * @param bucketName 存储桶名称
     * @return boolean
     */
    public Boolean bucketExists(String bucketName) {
        Boolean found;
        try {
            found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return found;
    }

    /**
     * 创建存储bucket
     *
     * @param bucketName 存储桶名称
     * @return Boolean
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除存储bucket
     *
     * @param bucketName 存储桶名称
     * @return Boolean
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 获取全部bucket
     *
     * @return 存储桶列表
     */
    public List<Bucket> getAllBuckets() {
        try {
            return minioClient.listBuckets();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文件上传
     *
     * @param file 文件
     * @return 文件对象名称
     */
    public String upload(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();

        System.out.println(originalFilename);

        if (!StringUtils.hasText(originalFilename)) {
            throw new RuntimeException();
        }

        String fileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
        String prefix = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
        String objectName = prefix + "/" + fileName;
        try {
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(minioConfig.getBucketName()).object(objectName)
                    .stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
            // 文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return objectName;
    }

    /**
     * 预览图片
     *
     * @param fileName 文件名称
     * @return 文件预览链接
     */
    public String preview(String fileName) {
        // 查看文件地址
        GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs
                .builder()
                .bucket(minioConfig.getBucketName())
                .object(fileName).method(Method.GET).build();
        try {
            String url = minioClient.getPresignedObjectUrl(build);
            return url;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文件下载
     *
     * @param fileName 文件名称
     * @param res      response
     */
    public void download(String fileName, HttpServletResponse res) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(minioConfig.getBucketName())
                .object(fileName).build();
        try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
            byte[] buf = new byte[1024];
            int len;
            try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
                while ((len = response.read(buf)) != -1) {
                    os.write(buf, 0, len);
                }
                os.flush();
                byte[] bytes = os.toByteArray();
                res.setCharacterEncoding("utf-8");
                res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
                try (ServletOutputStream stream = res.getOutputStream()) {
                    stream.write(bytes);
                    stream.flush();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 查看文件对象
     *
     * @return 存储bucket内文件对象信息
     */
    public List<Item> listObjects() {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(minioConfig.getBucketName()).build());
        List<Item> items = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                items.add(result.get());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return items;
    }

    /**
     * 删除
     *
     * @param fileName 文件名称
     * @return 是否删除成功
     */
    public boolean remove(String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(minioConfig.getBucketName())
                    .object(fileName)
                    .build());
        } catch (Exception e) {
            return false;
        }
        return true;
    }

}

5、Controller:

import com.cyw.miniodemo.config.MinioConfig;
import com.cyw.miniodemo.pojo.Rst;
import com.cyw.miniodemo.service.FileUploadService;
import com.cyw.miniodemo.utils.MinIOUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/api/file")
@AllArgsConstructor
public class FileUploadController {
    private MinioConfig minioConfig;
    private MinIOUtil minIOUtil;
    private FileUploadService fileUploadService;

    @GetMapping("/bucketExists")
    public Rst bucketExists(@RequestParam("bucketName") String bucketName) {
        Map<String, Object> map = new HashMap<>();
        map.put("bucketExists", minIOUtil.bucketExists(bucketName));
        return Rst.ok("查询成功", map);
    }

    @GetMapping("/makeBucket")
    public Rst makeBucket(@RequestParam("bucketName") String bucketName) {
        Map<String, Object> map = new HashMap<>();
        map.put("makeBucketSuccess", minIOUtil.makeBucket(bucketName));
        return Rst.ok("创建成功", map);
    }

    @GetMapping("/removeBucket")
    public Rst removeBucket(@RequestParam("bucketName") String bucketName) {
        Map<String, Object> map = new HashMap<>();
        map.put("deleteBucketSuccess", minIOUtil.removeBucket(bucketName));
        return Rst.ok("删除成功", map);
    }

    @GetMapping("/getAllBuckets")
    public Rst getAllBuckets() {
        Map<String, Object> map = new HashMap<>();
        map.put("buckets", minIOUtil.getAllBuckets());
        return Rst.ok("查询成功", map);
    }

    @PostMapping("/upload")
    public Rst upload(@RequestParam("file") MultipartFile file) {
        String objectName = minIOUtil.upload(file);

        if (objectName != null) {
            Map<String, Object> map = new HashMap<>();
            map.put("url", (minioConfig.getEndpoint() + "/" + minioConfig.getBucketName() + "/" + objectName));
            return Rst.ok("上传成功", map);
        }
        return Rst.fail("上传失败");
    }

    @GetMapping("/preview")
    public Rst preview(@RequestParam("fileName") String fileName) {
        Map<String, Object> map = new HashMap<>();
        map.put("url", minIOUtil.preview(fileName));
        return Rst.ok("预览成功", map);
    }

    @GetMapping("/download")
    public Rst download(@RequestParam("fileName") String fileName, HttpServletResponse resp) {
        minIOUtil.download(fileName, resp);
        return Rst.ok();
    }

    @PostMapping("/delete")
    public Rst remove(@RequestBody Map<String, String> params) {
        String url = params.get("url");
        String objName = url.substring(url.lastIndexOf(minioConfig.getBucketName() + "/") + minioConfig.getBucketName().length() + 1);
        log.info("删除对象: {}", objName);
        minIOUtil.remove(objName);
        return Rst.ok("删除成功");
    }
}

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力
在这里插入图片描述

### H20 算力优化及性能指标分析 #### INT8 和 FP16 的性能提升 英伟达H20在INT8和FP16精度下的表现尤为突出,其设计旨在最大化推理效率并降低延迟。相比前代产品,H20通过架构改进实现了更高的吞吐量,在INT8模式下提供了显著增强的TOPS(Tera Operations Per Second),而在FP16模式下则提升了TFLOPS(Tera Floating-point Operations Per Second)。这种优化使得H20成为机器学习模型部署的理想选择[^2]。 #### FLOPS 性能对比 就FLOPS而言,H20相较于前一代GPU有明显进步。具体来说,它不仅提高了单精度浮点运算能力(FP32),还大幅增强了混合精度计算的支持力度,这对于需要高精度与高效能平衡的应用场景尤为重要。此外,借助Tensor Core的新特性,H20能在特定工作负载中实现更高倍率的速度增益。 #### 功耗管理与 TDP 设计 功耗方面,尽管H20拥有更强悍的处理能力和更大的晶体管数量,但由于采用了先进的制程技术和高效的电源管理系统,整体能耗得到了有效控制。对于GB300、B300以及HGX平台上的配置版本,各自的热设计功率(TDP)均经过精心调整以适应不同的应用场景需求。例如,在数据中心环境中运行时,即使面对极高负荷的任务也能保持稳定而持久的表现。 #### 前代产品的比较 当我们将目光投向前几代NVIDIA GPU时可以发现,无论是从原始算力还是实际应用效能来看,H20都树立了一个新的标杆。特别是在针对AI训练和推理任务进行了专门调优之后,无论是在每瓦特性能还是单位面积内的计算密度上都有所突破。这表明相对于早期型号如V100或A100等,新一代硬件已经迈入了一个全新阶段——即更加注重可持续发展的同时追求极致性能。 ```python # 示例代码展示如何查询 NVIDIA GPU 的基本规格信息 (伪代码) import nvidia_smi nvidia_smi.nvmlInit() handle = nvidia_smi.nvmlDeviceGetHandleByIndex(0) info = nvidia_smi.nvmlDeviceGetPowerUsage(handle) print(f"Current Power Usage: {info / 1000} W") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

中北萌新程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值