《POSIX对于对象存储真的不合适吗?- CodesCode》

POSIX真的不适合用于对象存储吗?通过对MinIO、s3fs-fuse和JuiceFS的数据驱动分析来探讨

本篇文章的作者对MinIO的一篇文章提出了质疑,该文章认为POSIX不适用于对象存储。作者进行了涉及MinIO、s3fs-fuse和JuiceFS的全面测试。结果表明,MinIO和JuiceFS的性能很好,而s3fs-fuse则滞后。在小文件覆盖的场景中,JuiceFS FUSE-POSIX的性能优于其他解决方案。

最近,我在MinIO的博客上看到了一篇标题为“在对象存储之上放置文件系统是一个坏主意。这就是为什么”的文章。作者以s3fs-fuse为例,说明了使用可移植操作系统接口(POSIX)方法访问MinIO数据时遇到的性能挑战,并强调性能明显落后于直接访问MinIO。作者将这些性能问题归因于POSIX的固有缺陷。然而,我们的经验与这个结论有些不同。

POSIX是一个有用且被广泛采用的标准。按照POSIX开发的软件可以保证在不同操作系统之间的兼容性和可移植性。各个行业中的大多数应用程序都遵循POSIX标准。随着云计算、大数据和人工智能技术的进步,以及存储的数据量不断增加,对象存储等弹性存储解决方案的需求也越来越大。尽管像MinIO这样的对象存储提供了多种语言的软件开发工具包(SDK),但许多传统应用程序难以修改它们的代码以使用对象存储API。这就导致了各种存储产品在对象存储之上实现POSIX接口,以满足这种不灵活的需求。

业界中许多产品,如Ceph、JuiceFS和Weka,已成功在对象存储上实现了POSIX接口。这些解决方案拥有庞大的用户群体和众多成功案例,并在性能方面表现出色。

虽然POSIX确实存在一些复杂性,但相关问题并非不可克服。为了尊重并验证这些说法,我搭建了一个测试环境,在MinIO文章中使用相同的样本数据和测试方法,并进行了验证。

比较的产品和测试目标

为了进行综合评估,我将JuiceFS引入了比较中。

JuiceFS是一个开源的云原生分布式文件系统。它以对象存储作为数据存储层,并依赖一个单独的数据库来存储元数据。它提供了各种访问方法,包括POSIX API、S3 API、CSI Driver、HDFS API和WebDAV,以及独特的数据切分、缓存和并发读/写机制。JuiceFS是一个文件系统,与像s3fs-fuse这样的工具根本不同,后者只是将对象存储转换为POSIX协议。

通过将JuiceFS引入比较中,我旨在客观评估在对象存储之上实现类似POSIX协议的优势和劣势。

我在MinIO、JuiceFS和s3fs-fuse上进行了以下两个测试:

  • 写入一个10GB的文件
  • 用Pandas覆盖小文件

这三个解决方案都使用了一个在不同服务器上部署的MinIO实例作为底层存储。测试样本使用了一个10GB大小的文件,这个文件与MinIO文章中提到的CSV文件相同。

本文中的所有环境、软件、脚本和样本数据都附有完整的代码和说明,以确保您可以重现环境和测试结果。

服务器和测试环境设置

两个配置相同的云服务器:

  • 系统:Ubuntu 22.04 x64
  • CPU:8核
  • 内存:16GB
  • SSD:500GB
  • 网络:VPC

每台服务器的信息如下:

服务器 IP地址 目的
服务器A 172.16.254.18 部署MinIO实例
服务器B 172.16.254.19 作为测试环境

准备服务器A

1. 使用以下命令在服务器A上使用Docker部署MinIO:

# 创建一个专用目录并进入其中
mkdir minio && cd minio
# 创建一个配置文件
mkdir config
touch config/minio

“`html

2. 我把以下信息写入了config/minio文件:

MINIO_ROOT_USER=adminMINIO_ROOT_PASSWORD=abc123abcMINIO_VOLUMES="/mnt/data"

3. 我创建了MinIO容器:

sudo docker run -d --name minio \  -p 9000:9000 \  -p 9090:9090 \  -v /mnt/minio-data:/mnt/data \  -v ./config/minio:/etc/config.env \  -e "MINIO_CONFIG_ENV_FILE=/etc/config.env" \  --restart unless-stopped \  minio/minio server --console-address ":9090"

4. 在MinIO的Web控制台上,我预先创建了三个存储桶:

存储桶名称 用途
test-minio 用于测试MinIO
test-juicefs 用于测试JuiceFS
test-s3fs 用于测试s3fs-fuse

服务器B准备

1. 我下载了10GB的测试样本文件。

curl -LO https://data.cityofnewyork.us/api/views/t29m-gskq/rows.csv?accessType=DOWNLOAD

2. 我安装了mc客户端。

mc是由MinIO项目开发的命令行文件管理器,它能在Linux命令行上对本地和兼容S3的对象存储进行读写操作。使用mc cp命令可以在数据复制过程中实时显示进度和速度,便于观察各种测试。

注意:为了保持测试的公平性,所有三种方法都使用mc进行文件写入测试。

# 下载 mc.wget https://dl.min.io/client/mc/release/linux-amd64/mc# 检查 mc 版本.mc -vmc version RELEASE.2023-09-20T15-22-31Z (commit-id=38b8665e9e8649f98e6162bdb5163172e6ecc187)Runtime: go1.21.1 linux/amd64# 安装 mc.sudo install mc /usr/bin# 为 MinIO 设置别名.mc alias set my http://172.16.254.18:9000 admin abc123abc

3. 我下载了s3fs-fuse

sudo apt install s3fs# 检查版本号.s3fs --versionAmazon Simple Storage Service File System V1.93 (commit:unknown) with OpenSSL# 设置对象存储访问密钥.echo admin:abc123abc >  ~/.passwd-s3fs# 修改密钥文件权限.chmod 600  ~/.passwd-s3fs# 创建安装目录.mkdir mnt-s3fs# 挂载对象存储.s3fs test-s3fs:/ /root/mnt-s3fs -o url=http://172.16.254.18:9000 -o use_path_request_style

4. 我安装了JuiceFS。

我使用了官方脚本安装最新的JuiceFS社区版。

# 一键安装脚本curl -sSL https://d.juicefs.com/install | sh -# 检查版本号.juicefs versionjuicefs version 1.1.0+2023-09-04.08c4ae6

5. 我创建了一个文件系统。JuiceFS是使用前需要创建的文件系统,除了对象存储外,它还需要一个数据库作为元数据引擎。它支持各种数据库。这里,我使用常用的Redis作为元数据引擎。

注意:我在服务器A上安装了Redis,可以通过172.16.254.18:6379访问,没有设置密码。这里省略了安装过程,你可以参考Redis文档了解详情。

# 创建文件系统.juicefs format --storage minio \--bucket http://172.16.254.18:9000/test-juicefs \--access-key admin \--secret-key abc123abc \--trash-days 0 \redis://172.16.254.18/1 \myjfs

“`

6.我使用常用的POSIX和S3 API方法访问了JuiceFS并测试了其性能。

# 创建挂载目录mkdir ~/mnt-juicefs# 在POSIX模式下挂载文件系统juicefs mount redis://172.16.254.18/1 /root/mnt-juicefs# 使用S3 API方法访问文件系统export MINIO_ROOT_USER=adminexport MINIO_ROOT_PASSWORD=abc123abcjuicefs gateway redis://172.16.254.18/1 0.0.0.0:9000# 在mc中为JuiceFS S3 API设置别名.mc alias set juicefs http://172.16.254.18:9000 admin abc123abc

注意:JuiceFS网关也可以部署在服务器A或任何其他可访问互联网的服务器上,因为它公开了基于网络的S3 API。

测试和结果

这是我测试和结果的简要总结:

测试 MinIO S3FS-FUSE JuiceFS(FUSE) JuiceFS(S3网关)
写入一个10 GB文件 0m27.651s 3m6.380s 0m28.107s 0m28.091s
使用Pandas覆盖小文件 0.83秒 0.78秒 0.46秒 0.96秒

测试1:写入一个10 GB文件

此测试旨在评估写入大文件的性能。时间越短,性能越好。我使用time命令测量写入操作的持续时间,并提供了三个指标:

  • real:命令开始到结束的实际时间。包括所有等待时间,例如等待I/O操作完成、等待进程切换和等待资源。
  • user:在用户模式下执行的时间,表示用于执行用户代码的CPU时间。通常表示命令的计算工作量。
  • sys:在内核模式下执行的时间,表示用于执行内核代码的CPU时间。通常表示与系统调用相关的工作量,例如文件I/O和进程管理。

MinIO

我运行了以下命令执行复制测试:

time mc cp ./2018_Yellow_Taxi_Trip_Data.csv  my/test-minio/

将一个10 GB文件直接写入MinIO的结果:

real    0m27.651suser    0m10.767ssys 0m5.439s

s3fs-fuse

我运行了以下命令执行复制测试:

time mc cp ./2018_Yellow_Taxi_Trip_Data.csv /root/mnt-s3fs/

将一个10 GB文件直接写入s3fs-fuse的结果:

real 3m6.380suser 0m0.012ssys 0m5.459s

注意:尽管s3fs-fuse的写入时间为3分6秒,但没有像MinIO的文章中描述的写入失败。

JuiceFS

我使用POSIX和S3 API方法测试了JuiceFS的大文件写入性能:

# POSIX写入测试time mc cp ./2018_Yellow_Taxi_Trip_Data.csv /root/mnt-juicefs/# S3 API写入测试time mc cp ./2018_Yellow_Taxi_Trip_Data.csv  juicefs/myjfs/

JuiceFS使用POSIX写入一个10 GB文件的结果:

real    0m28.107suser    0m0.292ssys 0m6.930s

果汁文件系统(JuiceFS)S3 API写入10 GB文件的结果:

实际时间 0m28.091秒;用户时间 0m13.643秒;系统时间 0m4.142秒

大文件写入结果总结

以下图表展示了测试结果:

大文件写入结果(值越低越好)

测试结果显示,直接写入到MinIO和JuiceFS的性能相当,完成任务所需的时间约为30秒。相比之下,s3fs-fuse写入一个10 GB文件需要超过3分钟,大约比前两者慢六倍。

在写入大文件时,mc使用分块API将文件分块上传到S3接口。相反,s3fs-fuse只能在单个线程中向POSIX写入。JuiceFS还会自动将大文件分块并在顺序写入时并行写入MinIO,确保与直接MinIO写入相当的性能。而S3FS首先将文件写入缓存盘的单个线程,然后分块上传到MinIO,导致写入时间较长。

根据写入10 GB文件所需的30秒计算,平均速度为333 MB/s。这受限于云服务器SSD的带宽。这些测试结果表明,MinIO和JuiceFS都能最大程度利用本地SSD带宽,并随着服务器云盘和网络带宽的改进而提高性能。

测试二:使用Pandas覆盖小文件

这个测试评估了对象存储系统在小文件覆盖场景中的性能。每个软件的测试脚本略有不同。你可以在这里找到所有的脚本代码。

MinIO

我获取了测试脚本并运行了测试:

# 获取测试脚本curl -LO https://gist.githubusercontent.com/yuhr123/7acb7e6bb42fb0ff12f3ba64d2cdd7da/raw/30c748e20b56dec642a58f9cccd7ea6e213dab3c/pandas-minio.py# 运行测试python3 pandas-minio.py

结果如下:

执行时间:0.83秒

s3fs-fuse

我获取了测试脚本并运行了测试:

# 获取测试脚本curl -LO gist.githubusercontent.com/yuhr123/7acb7e6bb42fb0ff12f3ba64d2cdd7da/raw/30c748e20b56dec642a58f9cccd7ea6e213dab3c/pandas-s3fs.py# 运行测试python3 pandas-s3fs.py

测试结果如下:

执行时间:0.78秒

JuiceFS POSIX

我获取了测试脚本并运行了测试:

# 获取测试脚本curl -LO gist.githubusercontent.com/yuhr123/7acb7e6bb42fb0ff12f3ba64d2cdd7da/raw/30c748e20b56dec642a58f9cccd7ea6e213dab3c/pandas-juicefs-posix.py# 运行测试python3 pandas-juicefs-posix.py

测试结果如下:

执行时间:0.43秒

JuiceFS S3 API

我获取了测试脚本并运行了测试:

# 获取测试脚本curl -LO https://gist.githubusercontent.com/yuhr123/7acb7e6bb42fb0ff12f3ba64d2cdd7da/raw/30c748e20b56dec642a58f9cccd7ea6e213dab3c/pandas-juicefs-s3api.py# 运行测试python3 pandas-juicefs-s3api.py

测试结果如下:

执行时间:0.86秒

Pandas小文件覆盖的总结

下图展示了测试结果:

Pandas覆盖结果(较低的值更好)

在这个测试中,JuiceFS FUSE-POSIX显示了最快的速度,几乎比其他解决方案快了两倍。MinIO、s3fs-fuse和JuiceFS S3 Gateway的性能类似。从小文件覆盖的角度来看,POSIX接口证明比对象存储接口更高效,提供了更好的性能。

问题和分析

问题1:为什么S3FS这么慢?

分析:从测试数据可以清楚地看出,当写入相同的10GB文件时,S3FS花费了3分钟,而MinIO和JuiceFS分别在大约30秒内完成了任务。这种显著的性能差异主要是由不同的技术实现造成的。当s3fs-fuse写入文件时,它首先将文件写入本地临时文件,然后将其分块上传到对象存储中。如果本地磁盘空间不足,它将进行同步上传。它需要在本地磁盘和S3存储之间复制数据。因此,大文件或大量文件会导致性能下降。

此外,S3FS依靠底层对象存储的元数据管理能力。当它处理大量文件时,频繁与对象存储进行元数据检索的交互对性能有重大影响。简单来说,文件大小和写入S3FS的文件总数越大,性能开销比例就越大。

问题2:为什么JuiceFS更快?

分析:在测试中,JuiceFS和S3FS都利用FUSE进行读写。JuiceFS像MinIO一样充分利用了磁盘带宽,但没有遇到S3FS的性能问题。

答案在于它们各自的技术架构。在文件写入过程中,当数据经过FUSE层处理时,JuiceFS利用高并发、缓存和数据分块技术来减少FUSE层与底层对象存储之间的通信开销。这使得JuiceFS能够同时处理更多的文件读写请求,减少等待时间和传输延迟。

此外,JuiceFS采用了专用数据库(在本例中为Redis)来管理元数据。当处理特别大量的文件时,独立的元数据引擎可以有效减轻工作负载,实现更快的文件定位。

结论

以上的测试结果表明,将对象存储作为基础并在其上实现POSIX接口不一定会导致性能损失。无论是写入大文件还是小文件,JuiceFS的性能与直接使用MinIO写入相当,而底层对象存储性能没有因为POSIX访问而受到任何损耗。此外,在Pandas表覆盖方面,JuiceFS FUSE-POSIX的性能保持一致,甚至超过MinIO近一倍。

测试结果表明,某些软件(比如s3fs-fuse)在S3 API和POSIX接口之间进行转换时可能会遇到性能下降。虽然它可以是临时访问S3的方便工具,但对于稳定和高性能的长期使用,需要仔细研究和验证以选择更合适的解决方案。

对于简单的非结构化文件归档,直接使用MinIO或云对象存储是一个很好的选择。然而,对于涉及大规模数据存储和处理的场景,如AI模型训练、大数据分析、Kubernetes数据持久化和其他频繁的读写操作,JuiceFS的独立元数据管理、并发读写能力和缓存机制提供了卓越的性能。这是一个值得考虑的高性能文件系统解决方案。


Leave a Reply

Your email address will not be published. Required fields are marked *