rustFs和HDFS的压测

主要是想看rustFS和HDFS上的性能差异。

服务 版本 系统 磁盘 磁盘类型 存储节点数
hdfs 3.1.1 ubuntu16.04 disk(7TB)*12 机械硬盘 datanode 节点:4
rustfs 1.0.0-beta.12 ubuntu22.04 disk(7TB)*12 机械硬盘 节点:4

测试总结:
在纯粹的读写性能上,对象存储(如 AWS S3)并没有比 HDFS 更优秀,甚至在绝大多数大数据吞吐和延迟场景下,它的读写速度比 HDFS 更慢。 如果不考虑小文件对 NameNode 的压力,也不考虑云原生的弹性扩展,高并发访问场景,仅仅在物理机集群上对比纯粹的读写 IO 性能,HDFS 完胜对象存储。对象存储的流行,是因为它用可接受的性能损失,换取了高可用、低成本、免运维和无限扩容的巨大商业优势。

性能优势:对象存储的“王牌领域”
海量小文件,高吞吐顺序读写,存算分离架构,新硬件加持,云原生架构。

为什么现代对象存储实现了性能“反超”?

核心维度 HDFS 现代对象存储
单线程顺序吞吐 极高(尤其命中本地性时) 略低(受限于网络和 HTTP 协议)
超高并发吞吐 受限于 JVM 与 NameNode 锁竞争 极高(无状态网关,极易水平扩展)
元数据操作 (List) 极快 (全内存目录树) 依赖底层 KV 引擎,结构复杂时存在延迟
硬件压榨能力 较差 (无法完全吃透 NVMe) 极强 (结合异步 I/O 直接打满网卡/磁盘)
修改操作 支持 Append 仅支持覆盖重写 (Immutable)

结论:
如果你的集群还是普通的机械硬盘 + 千兆网络,HDFS 依然是处理大文件批处理的性能王者。
但如果你的底座已经升级到了 NVMe 阵列、万兆以上网络,并且使用了高度异步、非 Java 语言编写的现代对象存储引擎,那么在高并发和聚合吞吐量上,对象存储确实能展现出比 HDFS 更强大的压倒性优势。
NVMe 阵列:是指将多块基于 NVMe 协议的高性能固态硬盘(SSD)通过 RAID 技术或软件定义存储(SDS)整合在一起,形成的一个具备超高吞吐、超低延迟和高可靠性的统一存储池。

tensorflow环境安装

# pip安装tensorflow和tensorboard-plugin-profile
apt-get install python3-pip

pip install tensorflow==2.16.1
sudo pip install tensorboard-plugin-profile==2.16.0
pip install  tensorflow-io boto3 

# 查看版本
python3 -c "import tensorflow as tf; print('TF:', tf.__version__)"
python3 -c "import tensorflow_io as tfio; print('TF-IO:', tfio.__version__)"

小文件目录测试

读本地/data1/test_data/tianmu目录写rustfs和hdfs
子目录:4035
文件数:137891
总大小:145G

(写)小文件目录测试

Write RustFs压测

tensorFlow IO写rustfs

RustFS 写入压测 Python 代码(默认收起,点击展开)
import glob
import os
import time
from concurrent.futures import ThreadPoolExecutor, as_completed

# ================= 配置区域 =================
LOCAL_SRC_DIR = "/data1/test_data/tianmu"  # 本地真实小文件目录
TARGET_BASE_PATH = "s3://benchmark-bucket/tianmu_tf_raw"
AWS_ACCESS_KEY_ID = "accessrustfsadmin"
AWS_SECRET_ACCESS_KEY = "secretrustfsadmin"
S3_ENDPOINT = "rust-work.yy.com"
CONCURRENCY = 32  # 并发线程数(可根据需要调整为 16, 32, 64)
# ============================================

# TensorFlow S3 filesystem 依赖在导入时读取环境变量。
os.environ["S3_ENDPOINT"] = S3_ENDPOINT
os.environ["AWS_ACCESS_KEY_ID"] = AWS_ACCESS_KEY_ID
os.environ["AWS_SECRET_ACCESS_KEY"] = AWS_SECRET_ACCESS_KEY
os.environ["S3_USE_HTTPS"] = "0"

import tensorflow as tf  # noqa: E402
import tensorflow_io as tfio  # noqa: E402,F401


def upload_single_file(local_path):
    """使用 TensorFlow 原生 API 读取本地文件并写入 S3。"""
    relative_path = os.path.relpath(local_path, LOCAL_SRC_DIR)
    target_path = os.path.join(TARGET_BASE_PATH, relative_path)

    file_size = os.path.getsize(local_path)
    file_content = tf.io.read_file(local_path)
    tf.io.write_file(target_path, file_content)
    return file_size


def run_tf_benchmark():
    print(f"🔍 正在扫描本地目录: {LOCAL_SRC_DIR}...")
    all_files = [f for f in glob.glob(f"{LOCAL_SRC_DIR}/**/*", recursive=True) if os.path.isfile(f)]
    total_files = len(all_files)
    total_bytes = sum(os.path.getsize(f) for f in all_files)

    print(f"✅ 扫描完成!共计 {total_files} 个文件,总体积: {total_bytes / (1024**3):.2f} GB")
    print(f"🚀 开始 TensorFlow 纯净并发写 S3 压测 (目标: {TARGET_BASE_PATH} | 并发数: {CONCURRENCY})...")

    start_time = time.time()
    completed_files = 0
    uploaded_bytes = 0
    failed_files = 0

    with ThreadPoolExecutor(max_workers=CONCURRENCY) as executor:
        future_to_file = {executor.submit(upload_single_file, fp): fp for fp in all_files}

        for future in as_completed(future_to_file):
            try:
                file_size = future.result()
                uploaded_bytes += file_size
                completed_files += 1
            except Exception as e:
                failed_files += 1
                completed_files += 1
                if failed_files <= 5:
                    print(f"⚠️ 上传失败: {future_to_file[future]} -> {e}")

            if completed_files % 5000 == 0 or completed_files == total_files:
                elapsed = time.time() - start_time
                rps = completed_files / elapsed
                mbps = (uploaded_bytes / (1024**2)) / elapsed
                print(
                    f" 进度: [{completed_files}/{total_files}] | 失败: {failed_files} | "
                    f"实时 RPS: {rps:.1f} req/s | 吞吐: {mbps:.2f} MB/s"
                )

    total_time = time.time() - start_time
    avg_rps = total_files / total_time
    avg_mbps = (total_bytes / (1024**2)) / total_time

    print("=" * 60)
    print("🎉 TensorFlow 纯净小文件压测完成!")
    print(f"⏱️ 总耗时     : {total_time:.2f} 秒")
    print(f"⚡ 平均 QPS/RPS: {avg_rps:.2f} 文件/秒")
    print(f"🚀 平均吞吐   : {avg_mbps:.2f} MB/s")
    print(f"❌ 失败文件数 : {failed_files}")
    print("=" * 60)


if __name__ == "__main__":
    run_tf_benchmark()

output:

root@test-rustfs-5:/home/liangrui06/stress# python3 tf_raw_small_files_benchmark.py 
🔍 正在扫描本地目录: /data1/test_data/tianmu...
✅ 扫描完成!共计 137891 个文件,总体积: 144.52 GB
🚀 开始 TensorFlow 纯净并发写 S3 压测 (目标: s3://benchmark-bucket/tianmu_tf_raw | 并发数: 32)...

 进度: [5000/137891] | 失败: 0 | 实时 RPS: 7.2 req/s | 吞吐: 154.19 MB/s
 进度: [10000/137891] | 失败: 0 | 实时 RPS: 13.3 req/s | 吞吐: 142.59 MB/s
 进度: [15000/137891] | 失败: 0 | 实时 RPS: 18.5 req/s | 吞吐: 132.66 MB/s
 进度: [20000/137891] | 失败: 0 | 实时 RPS: 23.0 req/s | 吞吐: 124.03 MB/s
 进度: [25000/137891] | 失败: 0 | 实时 RPS: 26.9 req/s | 吞吐: 116.34 MB/s
 进度: [30000/137891] | 失败: 0 | 实时 RPS: 30.3 req/s | 吞吐: 109.66 MB/s
 进度: [35000/137891] | 失败: 0 | 实时 RPS: 33.4 req/s | 吞吐: 103.76 MB/s
 进度: [40000/137891] | 失败: 0 | 实时 RPS: 36.2 req/s | 吞吐: 98.51 MB/s
 进度: [45000/137891] | 失败: 0 | 实时 RPS: 38.6 req/s | 吞吐: 93.74 MB/s
 进度: [50000/137891] | 失败: 0 | 实时 RPS: 40.9 req/s | 吞吐: 89.46 MB/s
 进度: [55000/137891] | 失败: 0 | 实时 RPS: 43.0 req/s | 吞吐: 85.57 MB/s
 进度: [60000/137891] | 失败: 0 | 实时 RPS: 44.8 req/s | 吞吐: 82.02 MB/s
 进度: [65000/137891] | 失败: 0 | 实时 RPS: 46.6 req/s | 吞吐: 78.77 MB/s
 进度: [70000/137891] | 失败: 0 | 实时 RPS: 48.1 req/s | 吞吐: 75.65 MB/s
 进度: [75000/137891] | 失败: 0 | 实时 RPS: 49.5 req/s | 吞吐: 72.83 MB/s
 进度: [80000/137891] | 失败: 0 | 实时 RPS: 50.8 req/s | 吞吐: 70.20 MB/s
 进度: [85000/137891] | 失败: 0 | 实时 RPS: 52.1 req/s | 吞吐: 67.82 MB/s
 进度: [90000/137891] | 失败: 0 | 实时 RPS: 53.3 req/s | 吞吐: 65.60 MB/s
 进度: [95000/137891] | 失败: 0 | 实时 RPS: 54.4 req/s | 吞吐: 63.53 MB/s
 进度: [100000/137891] | 失败: 0 | 实时 RPS: 55.1 req/s | 吞吐: 61.40 MB/s
 进度: [105000/137891] | 失败: 0 | 实时 RPS: 55.7 req/s | 吞吐: 59.59 MB/s
 进度: [110000/137891] | 失败: 0 | 实时 RPS: 56.2 req/s | 吞吐: 57.92 MB/s
 进度: [115000/137891] | 失败: 0 | 实时 RPS: 56.6 req/s | 吞吐: 56.34 MB/s
 进度: [120000/137891] | 失败: 0 | 实时 RPS: 56.3 req/s | 吞吐: 56.50 MB/s
 进度: [125000/137891] | 失败: 0 | 实时 RPS: 55.9 req/s | 吞吐: 57.16 MB/s
 进度: [130000/137891] | 失败: 0 | 实时 RPS: 55.4 req/s | 吞吐: 58.03 MB/s
 进度: [135000/137891] | 失败: 0 | 实时 RPS: 55.0 req/s | 吞吐: 58.55 MB/s
 进度: [137891/137891] | 失败: 0 | 实时 RPS: 54.8 req/s | 吞吐: 58.86 MB/s
============================================================
🎉 TensorFlow 纯净小文件压测完成!
⏱️ 总耗时     : 2514.38 秒
⚡ 平均 QPS/RPS: 54.84 文件/秒
🚀 平均吞吐   : 58.86 MB/s
❌ 失败文件数 : 0
============================================================
pure virtual method called
terminate called without an active exception
Aborted

boto3 AWS_S3工具写rustfs

RustFS 写入压测 Python 代码(默认收起,点击展开)
import os
import glob
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
import boto3
from botocore.config import Config

# ================= 配置区域 =================
LOCAL_SRC_DIR = "/data1/test_data/tianmu"     # 本地真实小文件目录
S3_ENDPOINT = "http://rust-work.yy.com"       # RustFS 地址
AWS_ACCESS_KEY_ID = "accessrustfsadmin"
AWS_SECRET_ACCESS_KEY = "secretrustfsadmin"
BUCKET_NAME = "benchmark-bucket"
TARGET_PREFIX = "tianmu_boto3_raw"
CONCURRENCY = 32                              # 并发线程数(可根据需要调整为 16, 32, 64)
# ============================================

s3_config = Config(
    max_pool_connections=CONCURRENCY * 2,
    connect_timeout=10,
    read_timeout=120,
    retries={'max_attempts': 3, 'mode': 'standard'}
)

s3_client = boto3.client(
    's3',
    endpoint_url=S3_ENDPOINT,
    aws_access_key_id=AWS_ACCESS_KEY_ID,
    aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
    config=s3_config
)

def upload_single_file(local_path):
    """单文件纯净 PUT 上传函数"""
    relative_path = os.path.relpath(local_path, LOCAL_SRC_DIR)
    s3_key = f"{TARGET_PREFIX}/{relative_path}"
    
    file_size = os.path.getsize(local_path)
    with open(local_path, 'rb') as f:
        s3_client.put_object(
            Bucket=BUCKET_NAME,
            Key=s3_key,
            Body=f
        )
    return file_size

def run_boto3_benchmark():
    print(f"🔍 正在扫描本地目录: {LOCAL_SRC_DIR}...")
    all_files = [f for f in glob.glob(f"{LOCAL_SRC_DIR}/**/*", recursive=True) if os.path.isfile(f)]
    total_files = len(all_files)
    total_bytes = sum(os.path.getsize(f) for f in all_files)
    
    print(f"✅ 扫描完成!共计 {total_files} 个文件,总体积: {total_bytes / (1024**3):.2f} GB")
    print(f"🚀 开始 Boto3 纯净并发 PUT 压测 (目标 Bucket: {BUCKET_NAME} | 并发数: {CONCURRENCY})...")

    start_time = time.time()
    completed_files = 0
    uploaded_bytes = 0
    failed_files = 0

    with ThreadPoolExecutor(max_workers=CONCURRENCY) as executor:
        future_to_file = {executor.submit(upload_single_file, fp): fp for fp in all_files}
        
        for future in as_completed(future_to_file):
            try:
                file_size = future.result()
                uploaded_bytes += file_size
                completed_files += 1
            except Exception as e:
                failed_files += 1
                completed_files += 1
                if failed_files <= 5:  # 仅打印前 5 个错误避免刷屏
                    print(f"⚠️ 上传失败: {future_to_file[future]} -> {e}")

            if completed_files % 5000 == 0 or completed_files == total_files:
                elapsed = time.time() - start_time
                rps = completed_files / elapsed
                mbps = (uploaded_bytes / (1024**2)) / elapsed
                print(f" 进度: [{completed_files}/{total_files}] | 失败: {failed_files} | 实时 RPS: {rps:.1f} req/s | 吞吐: {mbps:.2f} MB/s")

    total_time = time.time() - start_time
    avg_rps = total_files / total_time
    avg_mbps = (total_bytes / (1024**2)) / total_time

    print("=" * 60)
    print(f"🎉 Boto3 纯净小文件压测完成!")
    print(f"⏱️ 总耗时     : {total_time:.2f} 秒")
    print(f"⚡ 平均 QPS/RPS: {avg_rps:.2f} 文件/秒")
    print(f"🚀 平均吞吐   : {avg_mbps:.2f} MB/s")
    print(f"❌ 失败文件数 : {failed_files}")
    print("=" * 60)

if __name__ == "__main__":
    run_boto3_benchmark()

output:

root@test-rustfs-5:/home/liangrui06/stress# python3 boto3_raw_small_files_benchmark.py 
🔍 正在扫描本地目录: /data1/test_data/tianmu...
✅ 扫描完成!共计 137891 个文件,总体积: 144.52 GB
🚀 开始 Boto3 纯净并发 PUT 压测 (目标 Bucket: benchmark-bucket | 并发数: 32)...
 进度: [5000/137891] | 失败: 0 | 实时 RPS: 6.1 req/s | 吞吐: 131.94 MB/s
 进度: [10000/137891] | 失败: 0 | 实时 RPS: 11.7 req/s | 吞吐: 125.87 MB/s
 进度: [15000/137891] | 失败: 0 | 实时 RPS: 16.7 req/s | 吞吐: 120.26 MB/s
 进度: [20000/137891] | 失败: 0 | 实时 RPS: 21.3 req/s | 吞吐: 115.17 MB/s
 进度: [25000/137891] | 失败: 0 | 实时 RPS: 25.5 req/s | 吞吐: 110.51 MB/s
 进度: [30000/137891] | 失败: 0 | 实时 RPS: 29.4 req/s | 吞吐: 106.25 MB/s
 进度: [35000/137891] | 失败: 0 | 实时 RPS: 32.9 req/s | 吞吐: 102.31 MB/s
 进度: [40000/137891] | 失败: 0 | 实时 RPS: 36.2 req/s | 吞吐: 98.67 MB/s
 进度: [45000/137891] | 失败: 0 | 实时 RPS: 39.3 req/s | 吞吐: 95.34 MB/s
 进度: [50000/137891] | 失败: 0 | 实时 RPS: 42.2 req/s | 吞吐: 92.23 MB/s
 进度: [55000/137891] | 失败: 0 | 实时 RPS: 44.8 req/s | 吞吐: 89.34 MB/s
 进度: [60000/137891] | 失败: 0 | 实时 RPS: 47.4 req/s | 吞吐: 86.65 MB/s
 进度: [65000/137891] | 失败: 0 | 实时 RPS: 49.7 req/s | 吞吐: 84.12 MB/s
 进度: [70000/137891] | 失败: 0 | 实时 RPS: 52.0 req/s | 吞吐: 81.75 MB/s
 进度: [75000/137891] | 失败: 0 | 实时 RPS: 54.1 req/s | 吞吐: 79.52 MB/s
 进度: [80000/137891] | 失败: 0 | 实时 RPS: 56.1 req/s | 吞吐: 77.39 MB/s
 进度: [85000/137891] | 失败: 0 | 实时 RPS: 57.9 req/s | 吞吐: 75.40 MB/s
 进度: [90000/137891] | 失败: 0 | 实时 RPS: 59.7 req/s | 吞吐: 73.52 MB/s
 进度: [95000/137891] | 失败: 0 | 实时 RPS: 61.4 req/s | 吞吐: 71.73 MB/s
 进度: [100000/137891] | 失败: 0 | 实时 RPS: 63.0 req/s | 吞吐: 70.12 MB/s
 进度: [105000/137891] | 失败: 0 | 实时 RPS: 64.2 req/s | 吞吐: 68.73 MB/s
 进度: [110000/137891] | 失败: 0 | 实时 RPS: 65.4 req/s | 吞吐: 67.44 MB/s
 进度: [115000/137891] | 失败: 0 | 实时 RPS: 66.5 req/s | 吞吐: 66.20 MB/s
 进度: [120000/137891] | 失败: 0 | 实时 RPS: 66.3 req/s | 吞吐: 66.48 MB/s
 进度: [125000/137891] | 失败: 0 | 实时 RPS: 65.8 req/s | 吞吐: 67.26 MB/s
 进度: [130000/137891] | 失败: 0 | 实时 RPS: 65.2 req/s | 吞吐: 68.29 MB/s
 进度: [135000/137891] | 失败: 0 | 实时 RPS: 64.8 req/s | 吞吐: 68.87 MB/s
 进度: [137891/137891] | 失败: 0 | 实时 RPS: 64.5 req/s | 吞吐: 69.27 MB/s
============================================================
🎉 Boto3 纯净小文件压测完成!
⏱️ 总耗时     : 2136.28 秒
⚡ 平均 QPS/RPS: 64.55 文件/秒
🚀 平均吞吐   : 69.27 MB/s
❌ 失败文件数 : 0

tensorFlow IO和boto3对比

TensorFlow IO 每分钟http请求总数在18k左右,服务端有大量http_transport_failed,但最终于可能是重试全部上传成功。
alt text
boto3 每分钟http请求总数是7k左右,没有异常,正常上传完成。
alt text

总结对照

维度 boto3 (S3 原生 API) TensorFlow (s3:// 文件系统抽象)
定位 对象存储客户端 伪装成 POSIX 磁盘的 S3 桥接驱动
设计逻辑 键值对模型:直接把 key 和 data 给 S3,由 S3 200 OK 担保 文件树模型:必须模拟目录树、路径检查、文件状态检查
写文件过程 PUT -> 收到 200 OK 即成功 (1 次 HTTP) HEAD/LIST 探测 -> PUT 临时文件 -> HEAD 校验 -> COPY/DELETE (2~4 次 HTTP)
开销与 QPS HTTP 请求极少,网络和磁盘吞吐高 产生的 HTTP 请求数急剧膨胀,容易造成服务端 HTTP 连接池和限流压力

提示: 上传文件不要用tensorFlow_io.write_file 它会在内部使用object_list,小文件过多会频繁的调用object_list接口
默认值太小 DEFAULT_INTERNODE_RPC_REPLAY_CACHE_CAPACITY: usize = 1_048_576; 会让http很多交互rustfs异常
RUSTFS_INTERNODE_RPC_REPLAY_CACHE_CAPACITY=调大默认值

测试tensorFlow Write HDFS

环境安装:

apt-get install openjdk-8-jre

tensorFlow 读写hdfs的配置,最后是放到/etc/profile.d/tensorflow_hdfs.sh中,然后执行source /etc/profile.d/tensorflow_hdfs.sh

# java 环境变量配置
export JAVA_HOME=//usr/lib/jvm/java-8-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH

# 1. 确保指定 Hadoop 安装根路径(如果你的 Hadoop 在 /usr/local/hadoop)
export HADOOP_HOME=/usr/local/hadoop
export PATH=$HADOOP_HOME/bin:$PATH

# 2. 补全原生 C 库路径(包含 libhdfs.so)+ Java libjvm.so
JAVA_LIBJVM_DIR=$(dirname $(find $JAVA_HOME/ -name "libjvm.so" 2>/dev/null | head -n 1))
HADOOP_NATIVE_DIR=$HADOOP_HOME/lib/native

export LD_LIBRARY_PATH=$HADOOP_NATIVE_DIR:$JAVA_LIBJVM_DIR:$LD_LIBRARY_PATH

# 3. 将 Hadoop 配置文件目录(core-site.xml, hdfs-site.xml 所在的目录)强制插入 CLASSPATH 最前面
#  注意:如果你的配置文件在 /etc/hadoop/conf,请改为 /etc/hadoop/conf
HADOOP_CONF_DIR=${HADOOP_CONF_DIR:-/etc/hadoop/conf}
export CLASSPATH=$HADOOP_CONF_DIR:$(hadoop classpath --glob)

# 4. 显式指定 Kerberos 认证
export HADOOP_SECURITY_AUTHENTICATION="kerberos"

测试代码
python 代码

HDFS 写入压测 Python 代码(默认收起,点击展开)
import glob
import os
import subprocess
import time
from concurrent.futures import ThreadPoolExecutor, as_completed

# ================= 配置区域 =================
LOCAL_SRC_DIR = "/data1/test_data/tianmu"  # 本地真实小文件目录

# HDFS 配置
HDFS_NAMENODE = "hdfs://yycluster01"
HDFS_BASE_DIR = f"{HDFS_NAMENODE}/tmp/tensorflow_bench"

KEYTAB_FILE = "/home/liangrui06/test-hiido2.keytab"
PRINCIPAL = "test-hiido2@TESTCLUSTER.COM"

CONCURRENCY = 32  # 并发线程数(与 RustFS 压测保持一致)
# ============================================


def setup_hadoop_env():
    """配置 TensorFlow HDFS 文件系统所需的 CLASSPATH 与 LD_LIBRARY_PATH。"""
    # 尝试自动定位 hadoop 可执行文件并获取 classpath
    if "CLASSPATH" not in os.environ:
        try:
            res = subprocess.run("hadoop classpath --glob", shell=True, capture_output=True, text=True)
            if res.returncode == 0 and res.stdout.strip():
                os.environ["CLASSPATH"] = res.stdout.strip()
                print("✅ 自动获取并配置 HADOOP CLASSPATH 成功。")
        except Exception as e:
            print(f"⚠️ 无法自动获取 hadoop classpath: {e}")

    # 尝试补充 JAVA_HOME 相关的 libjvm.so
    java_home = os.environ.get("JAVA_HOME")
    if java_home:
        jvm_paths = [
            os.path.join(java_home, "jre/lib/amd64/server"),
            os.path.join(java_home, "lib/server"),
        ]
        for jvm_path in jvm_paths:
            if os.path.exists(jvm_path):
                current_ld = os.environ.get("LD_LIBRARY_PATH", "")
                if jvm_path not in current_ld:
                    os.environ["LD_LIBRARY_PATH"] = f"{jvm_path}:{current_ld}"
                    print(f"✅ 自动注入 libjvm 路径到 LD_LIBRARY_PATH: {jvm_path}")
                break


def do_kinit():
    """在脚本启动前使用 keytab 进行 Kerberos 认证。"""
    if os.path.exists(KEYTAB_FILE):
        print(f"🔑 正在使用 Keytab 进行 Kerberos 认证: {PRINCIPAL}...")
        cmd = f"kinit -kt {KEYTAB_FILE} {PRINCIPAL}"
        res = subprocess.run(cmd, shell=True, capture_output=True, text=True)
        if res.returncode == 0:
            print("✅ Kerberos 认证成功!")
        else:
            print(f"⚠️ Kerberos 认证 Warning/Error: {res.stderr.strip()}")
    else:
        print(f"⚠️ 未找到 Keytab 文件: {KEYTAB_FILE},将尝试直接依赖当前环境的 kinit 票据。")


# 设置环境与 Kerberos 认证
setup_hadoop_env()
do_kinit()

import tensorflow as tf  # noqa: E402
import tensorflow_io as tfio  # noqa: E402,F401


def upload_single_file(local_path):
    """使用 TensorFlow 原生 API 读取本地文件并写入 HDFS。"""
    relative_path = os.path.relpath(local_path, LOCAL_SRC_DIR)
    target_path = os.path.join(HDFS_BASE_DIR, relative_path)

    file_size = os.path.getsize(local_path)
    file_content = tf.io.read_file(local_path)
    tf.io.write_file(target_path, file_content)
    return file_size


def run_tf_hdfs_benchmark():
    print(f"🔍 正在扫描本地目录: {LOCAL_SRC_DIR}...")
    all_files = [f for f in glob.glob(f"{LOCAL_SRC_DIR}/**/*", recursive=True) if os.path.isfile(f)]
    total_files = len(all_files)
    total_bytes = sum(os.path.getsize(f) for f in all_files)

    print(f"✅ 扫描完成!共计 {total_files} 个文件,总体积: {total_bytes / (1024**3):.2f} GB")
    print(f"🚀 开始 TensorFlow 纯净并发写 HDFS 压测 (目标: {HDFS_BASE_DIR} | 并发数: {CONCURRENCY})...")

    start_time = time.time()
    completed_files = 0
    uploaded_bytes = 0
    failed_files = 0

    with ThreadPoolExecutor(max_workers=CONCURRENCY) as executor:
        future_to_file = {executor.submit(upload_single_file, fp): fp for fp in all_files}

        for future in as_completed(future_to_file):
            try:
                file_size = future.result()
                uploaded_bytes += file_size
                completed_files += 1
            except Exception as e:
                failed_files += 1
                completed_files += 1
                if failed_files <= 5:
                    print(f"⚠️ 上传失败: {future_to_file[future]} -> {e}")

            if completed_files % 5000 == 0 or completed_files == total_files:
                elapsed = time.time() - start_time
                rps = completed_files / elapsed
                mbps = (uploaded_bytes / (1024**2)) / elapsed
                print(
                    f" 进度: [{completed_files}/{total_files}] | 失败: {failed_files} | "
                    f"实时 RPS: {rps:.1f} req/s | 吞吐: {mbps:.2f} MB/s"
                )

    total_time = time.time() - start_time
    avg_rps = total_files / total_time
    avg_mbps = (total_bytes / (1024**2)) / total_time

    print("=" * 60)
    print("🎉 TensorFlow 纯净小文件 HDFS 压测完成!")
    print(f"⏱️ 总耗时     : {total_time:.2f} 秒")
    print(f"⚡ 平均 QPS/RPS: {avg_rps:.2f} 文件/秒")
    print(f"🚀 平均吞吐   : {avg_mbps:.2f} MB/s")
    print(f"❌ 失败文件数 : {failed_files}")
    print("=" * 60)


if __name__ == "__main__":
    run_tf_hdfs_benchmark()

运行输出:

python3 tf_hdfs_small_files_benchmark.py 
🔑 正在使用 Keytab 进行 Kerberos 认证: test-hiido2@TESTCLUSTER.COM...
✅ Kerberos 认证成功!
2026-08-05 16:49:13.112546: I external/local_tsl/tsl/cuda/cudart_stub.cc:32] Could not find cuda drivers on your machine, GPU will not be used.
2026-08-05 16:49:13.120453: I external/local_tsl/tsl/cuda/cudart_stub.cc:32] Could not find cuda drivers on your machine, GPU will not be used.
2026-08-05 16:49:13.260260: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 AVX512F FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
2026-08-05 16:49:14.811873: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT
🔍 正在扫描本地目录: /data1/test_data/tianmu...
✅ 扫描完成!共计 137891 个文件,总体积: 144.52 GB
🚀 开始 TensorFlow 纯净并发写 HDFS 压测 (目标: hdfs://yycluster01/tmp/tensorflow_bench | 并发数: 32)...
...
 进度: [5000/137891] | 失败: 0 | 实时 RPS: 7.6 req/s | 吞吐: 163.36 MB/s
 进度: [10000/137891] | 失败: 0 | 实时 RPS: 15.2 req/s | 吞吐: 163.29 MB/s
 进度: [15000/137891] | 失败: 0 | 实时 RPS: 22.6 req/s | 吞吐: 162.42 MB/s
 进度: [20000/137891] | 失败: 0 | 实时 RPS: 29.9 req/s | 吞吐: 161.35 MB/s
 进度: [25000/137891] | 失败: 0 | 实时 RPS: 37.1 req/s | 吞吐: 160.60 MB/s
 进度: [30000/137891] | 失败: 0 | 实时 RPS: 44.2 req/s | 吞吐: 159.80 MB/s
 进度: [35000/137891] | 失败: 0 | 实时 RPS: 51.2 req/s | 吞吐: 159.14 MB/s
 进度: [40000/137891] | 失败: 0 | 实时 RPS: 58.1 req/s | 吞吐: 158.28 MB/s
 进度: [45000/137891] | 失败: 0 | 实时 RPS: 64.9 req/s | 吞吐: 157.46 MB/s
 进度: [50000/137891] | 失败: 0 | 实时 RPS: 71.0 req/s | 吞吐: 155.35 MB/s
 进度: [55000/137891] | 失败: 0 | 实时 RPS: 77.7 req/s | 吞吐: 154.73 MB/s
 进度: [60000/137891] | 失败: 0 | 实时 RPS: 84.3 req/s | 吞吐: 154.17 MB/s
 进度: [65000/137891] | 失败: 0 | 实时 RPS: 90.8 req/s | 吞吐: 153.51 MB/s
 进度: [70000/137891] | 失败: 0 | 实时 RPS: 97.2 req/s | 吞吐: 152.93 MB/s
 进度: [75000/137891] | 失败: 0 | 实时 RPS: 103.6 req/s | 吞吐: 152.36 MB/s
 进度: [80000/137891] | 失败: 0 | 实时 RPS: 109.9 req/s | 吞吐: 151.79 MB/s
 进度: [85000/137891] | 失败: 0 | 实时 RPS: 116.0 req/s | 吞吐: 151.03 MB/s
 进度: [90000/137891] | 失败: 0 | 实时 RPS: 122.1 req/s | 吞吐: 150.31 MB/s
 进度: [95000/137891] | 失败: 0 | 实时 RPS: 128.2 req/s | 吞吐: 149.75 MB/s
 进度: [100000/137891] | 失败: 0 | 实时 RPS: 134.0 req/s | 吞吐: 149.17 MB/s
 进度: [105000/137891] | 失败: 0 | 实时 RPS: 138.7 req/s | 吞吐: 148.42 MB/s
 进度: [110000/137891] | 失败: 0 | 实时 RPS: 143.4 req/s | 吞吐: 147.95 MB/s
 进度: [115000/137891] | 失败: 0 | 实时 RPS: 148.1 req/s | 吞吐: 147.34 MB/s
 进度: [120000/137891] | 失败: 0 | 实时 RPS: 145.4 req/s | 吞吐: 145.82 MB/s
 进度: [125000/137891] | 失败: 0 | 实时 RPS: 141.2 req/s | 吞吐: 144.37 MB/s
 进度: [130000/137891] | 失败: 0 | 实时 RPS: 136.4 req/s | 吞吐: 142.87 MB/s
 进度: [135000/137891] | 失败: 0 | 实时 RPS: 138.4 req/s | 吞吐: 147.20 MB/s
 进度: [137891/137891] | 失败: 0 | 实时 RPS: 140.6 req/s | 吞吐: 150.90 MB/s
============================================================
🎉 TensorFlow 纯净小文件 HDFS 压测完成!
⏱️ 总耗时     : 980.70 秒
⚡ 平均 QPS/RPS: 140.60 文件/秒
🚀 平均吞吐   : 150.90 MB/s
❌ 失败文件数 : 0
============================================================

因rustfs默认是EC存储,我们也对HDFS进行EC存储测试,总耗时还是比rustfs快。
HDFS EC存储,output:

root@test-rustfs-5:/home/liangrui06/stress# hdfs ec -getPolicy -path hdfs://yycluster01/rs_3_2/tensorflow_bench
RS-3-2-1024k
@test-rustfs-5:/home/liangrui06/stress# python3 tf_hdfs_small_files_benchmark.py 
🔑 正在使用 Keytab 进行 Kerberos 认证: test-hiido2@TESTCLUSTER.COM...
✅ Kerberos 认证成功!
2026-08-06 10:46:31.254873: I external/local_tsl/tsl/cuda/cudart_stub.cc:32] Could not find cuda drivers on your machine, GPU will not be used.
2026-08-06 10:46:31.262804: I external/local_tsl/tsl/cuda/cudart_stub.cc:32] Could not find cuda drivers on your machine, GPU will not be used.
2026-08-06 10:46:31.401894: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 AVX512F FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
2026-08-06 10:46:32.908987: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT
🔍 正在扫描本地目录: /data1/test_data/tianmu...
✅ 扫描完成!共计 137891 个文件,总体积: 144.52 GB
🚀 开始 TensorFlow 纯净并发写 HDFS 压测 (目标: hdfs://yycluster01/rs_3_2/tensorflow_bench | 并发数: 32)...
2026-08-06 10:46:39,452 WARN erasurecode.ErasureCodeNative: ISA-L support is not available in your platform... using builtin-java codec where applicable
 进度: [5000/137891] | 失败: 0 | 实时 RPS: 7.1 req/s | 吞吐: 152.60 MB/s
 进度: [10000/137891] | 失败: 0 | 实时 RPS: 14.0 req/s | 吞吐: 150.62 MB/s
 进度: [15000/137891] | 失败: 0 | 实时 RPS: 20.7 req/s | 吞吐: 148.80 MB/s
 进度: [20000/137891] | 失败: 0 | 实时 RPS: 27.3 req/s | 吞吐: 147.43 MB/s
 进度: [25000/137891] | 失败: 0 | 实时 RPS: 33.7 req/s | 吞吐: 146.08 MB/s
 进度: [30000/137891] | 失败: 0 | 实时 RPS: 40.0 req/s | 吞吐: 144.60 MB/s
 进度: [35000/137891] | 失败: 0 | 实时 RPS: 46.2 req/s | 吞吐: 143.38 MB/s
 进度: [40000/137891] | 失败: 0 | 实时 RPS: 52.2 req/s | 吞吐: 142.17 MB/s
 进度: [45000/137891] | 失败: 0 | 实时 RPS: 58.0 req/s | 吞吐: 140.83 MB/s
 进度: [50000/137891] | 失败: 0 | 实时 RPS: 63.7 req/s | 吞吐: 139.41 MB/s
 进度: [55000/137891] | 失败: 0 | 实时 RPS: 69.4 req/s | 吞吐: 138.20 MB/s
 进度: [60000/137891] | 失败: 0 | 实时 RPS: 74.9 req/s | 吞吐: 137.04 MB/s
 进度: [65000/137891] | 失败: 0 | 实时 RPS: 80.3 req/s | 吞吐: 135.85 MB/s
 进度: [70000/137891] | 失败: 0 | 实时 RPS: 85.6 req/s | 吞吐: 134.58 MB/s
 进度: [75000/137891] | 失败: 0 | 实时 RPS: 90.8 req/s | 吞吐: 133.55 MB/s
 进度: [80000/137891] | 失败: 0 | 实时 RPS: 95.9 req/s | 吞吐: 132.42 MB/s
 进度: [85000/137891] | 失败: 0 | 实时 RPS: 100.8 req/s | 吞吐: 131.16 MB/s
 进度: [90000/137891] | 失败: 0 | 实时 RPS: 105.6 req/s | 吞吐: 130.02 MB/s
 进度: [95000/137891] | 失败: 0 | 实时 RPS: 110.5 req/s | 吞吐: 129.00 MB/s
 进度: [100000/137891] | 失败: 0 | 实时 RPS: 115.1 req/s | 吞吐: 128.14 MB/s
 进度: [105000/137891] | 失败: 0 | 实时 RPS: 119.1 req/s | 吞吐: 127.48 MB/s
 进度: [110000/137891] | 失败: 0 | 实时 RPS: 123.3 req/s | 吞吐: 127.16 MB/s
 进度: [115000/137891] | 失败: 0 | 实时 RPS: 127.5 req/s | 吞吐: 126.85 MB/s
 进度: [120000/137891] | 失败: 0 | 实时 RPS: 126.5 req/s | 吞吐: 126.85 MB/s
 进度: [125000/137891] | 失败: 0 | 实时 RPS: 124.2 req/s | 吞吐: 126.99 MB/s
 进度: [130000/137891] | 失败: 0 | 实时 RPS: 121.3 req/s | 吞吐: 127.09 MB/s
 进度: [135000/137891] | 失败: 0 | 实时 RPS: 119.4 req/s | 吞吐: 126.94 MB/s
 进度: [137891/137891] | 失败: 0 | 实时 RPS: 118.1 req/s | 吞吐: 126.70 MB/s
============================================================
🎉 TensorFlow 纯净小文件 HDFS 压测完成!
⏱️ 总耗时     : 1168.02 秒
⚡ 平均 QPS/RPS: 118.06 文件/秒
🚀 平均吞吐   : 126.70 MB/s
❌ 失败文件数 : 0
============================================================

写测试总结

也研究了一些官方对比指标,在写数据上hdfs确实比对象存储快。
原因: 在生成阶段,S3 协议暂存提交器处于劣势,因为提交器会将数据暂存到 RAM 或磁盘中,然后再上传到 对象服务(因为没有元数据管理)。而对于 HDFS 和 S3A Magic 提交器来说,暂存性能损失并不存在。
minio的测试
alt text

minio:对于 Terasort 算法,HDFS 生成步骤的性能比 MinIO 快 2.1 倍。对于 Sort and Wordcount 算法,HDFS 生成步骤的性能比 MinIO 快 1.9 倍
但是总体测试:其它环节minio比HDFS要快,以整体时间维度超过了HDFS。

以下是基于同一套数据集(137,891 个文件,总体积 144.52 GB,并发数 32)在 HDFS 与 RustFS 上的完整压测对比总结报告。
一、 压测对比数据汇总表

测试场景 存储后端 协议与写入方式 数据冗余模式 总耗时 (秒) 平均 RPS (文件/秒) 平均吞吐 (MB/s) 失败数 性能相对基准 (以HDFS非EC为100%)
场景 1 HDFS TensorFlow Native (Hadoop RPC) 副本/非 EC (3 副本) 980.70 140.60 150.90 0 100.0% (最快)
场景 2 HDFS TensorFlow Native (Hadoop RPC) EC (RS-3-2-1024k) 1168.02 118.06 126.70 0 83.9%
场景 3 RustFS Boto3 (原生 S3 REST PUT) EC (默认分布式 EC) 2136.28 64.55 69.27 0 45.9%
场景 4 RustFS TensorFlow S3 IO (tf.io.write_file) EC (默认分布式 EC) 2514.38 54.84 58.86 0 39.0%

二、 核心结论与机制诊断

  1. 存储协议与元数据提交机制(最根本差异)
    HDFS (Native Streaming):HDFS 拥有独立的 NameNode 进行元数据管理。客户端在写数据时是纯流式(Direct Streaming)直接推送到 DataNode,写完后在 NameNode 完成 O(1) 的轻量提交,完全不需要客户端暂存(Staging)。 S3 / RustFS (Object Staging & REST):正如 MinIO 官方性能报告所指出,因为 S3 协议本身缺乏 POSIX 级的原子 rename 和目录节点语义,S3 客户端/驱动在写入时必须先在本地 RAM 或 /tmp 磁盘中进行暂存(Staging),然后再通过 HTTP REST API 进行上传,存在天然的额外延迟与性能损失。
  2. TensorFlow S3 IO 驱动的多余探测开销
    Boto3 (64.55 RPS) 采用的是纯粹的 S3 REST API,一个文件对应 1 次 HTTP PUT,无多余逻辑。 TensorFlow S3 IO (54.84 RPS) 是将 C++ 的 FileSystem 抽象强行桥接到 S3 REST 上。为了模拟文件系统语义,每次写入前/后会发送大量的 HEAD / GET / ListObjects 请求探测路径状态(监控显示额外产生了约 36% 的 GET 请求),导致总 HTTP 请求量飙升至 Boto3 的 2.5 倍以上,进一步拖慢了速度。
  3. EC(纠删码)计算与网络开销
    在 HDFS 内部,启用 RS-3-2 EC 后,耗时从 980 秒增加到 1168 秒(吞吐下降约 16%),这反映了纯粹的 Reed-Solomon 纠删码矩阵计算与分片分发开销。 但即使带上 EC 计算,HDFS 仍然比 RustFS 最快的方式(Boto3)快了近 1.8 倍,证明在小文件写入场景下,“S3 客户端 Staging 开销 + HTTP/REST 报文头开销” 对性能的影响远大于“纠删码计算”本身。

(读)小文件目录测试

脚本示例:
hdfs get目录

HDFS get目录默认收起,点击展开)
#!/usr/bin/env bash
set -e

# nohup ./benchmark_hdfs_cp.sh > hdfs_benchmark_cp.log 2>&1 &
# ============================================
echo "🚀 开始执行 hdfs cp 压测..."
echo "------------------------------------------------------------"

# 记录高精度开始时间 (秒.纳秒)
START_TIME=$(date +%s.%N)

# 执行 hdfs 命令
hadoop fs -get -t 64 hdfs://yycluster01/tmp/tensorflow_bench /data1/down/hdfs_tianmu_boto3_raw
# hadoop fs -get -t 64 hdfs://yycluster01/rs_3_2/tensorflow_bench /data1/down/ec_hdfs_tianmu_boto3_raw

# 记录高精度结束时间
END_TIME=$(date +%s.%N)

# 计算总耗时 (秒)
ELAPSED=$(awk -v start="${START_TIME}" -v end="${END_TIME}" 'BEGIN { printf "%.2f", end - start }')

# 统计下载后的本地目标目录实际字节数和文件总数
TOTAL_BYTES=$(du -sb /data1/down/ec_hdfs_tianmu_boto3_raw | awk '{print $1}')
TOTAL_FILES=$(find /data1/down/ec_hdfs_tianmu_boto3_raw -type f | wc -l)

# 计算每秒吞吐与速率
BYTES_PER_SEC=$(awk -v b="${TOTAL_BYTES}" -v e="${ELAPSED}" 'BEGIN { printf "%.2f", b / e }')
MBPS=$(awk -v b="${TOTAL_BYTES}" -v e="${ELAPSED}" 'BEGIN { printf "%.2f", (b / 1024 / 1024) / e }')
RPS=$(awk -v f="${TOTAL_FILES}" -v e="${ELAPSED}" 'BEGIN { printf "%.2f", f / e }')
TOTAL_GB=$(awk -v b="${TOTAL_BYTES}" 'BEGIN { printf "%.2f", b / 1024 / 1024 / 1024 }')

echo "============================================================"
echo "🎉 hdfs   cp 下载压测完成!"
echo "⏱️ 总耗时         : ${ELAPSED} 秒"
echo "📂 下载文件总数   : ${TOTAL_FILES} 个"
echo "📦 传输数据总量   : ${TOTAL_BYTES} 字节 (${TOTAL_GB} GB)"
echo "⚡ 每秒传输字节数 : ${BYTES_PER_SEC} Bytes/s"
echo "🚀 平均吞吐速率   : ${MBPS} MB/s"
echo "⚡ 平均 RPS       : ${RPS} 文件/秒"
echo "============================================================"

执行:./benchmark_hdfs_cp.sh

output...
🚀 开始执行 hdfs cp 下载压测完成!
------------------------------------------------------------
============================================================
🎉 rc cp 下载压测完成!
⏱️ 总耗时         : 608.23 秒
📂 下载文件总数   : 137891 个
📦 传输数据总量   : 155191190027 字节 (144.53 GB)
⚡ 每秒传输字节数 : 255152146.44 Bytes/s
🚀 平均吞吐速率   : 243.33 MB/s
⚡ 平均 RPS       : 226.71 文件/秒
============================================================

============================================================
🎉 hdfs ec cp 下载压测完成!
⏱️ 总耗时         : 607.93 秒
📂 下载文件总数   : 137891 个
📦 传输数据总量   : 155191247371 字节 (144.53 GB)
⚡ 每秒传输字节数 : 255278152.70 Bytes/s
🚀 平均吞吐速率   : 243.45 MB/s
⚡ 平均 RPS       : 226.82 文件/秒
============================================================

rustfs rc get目录

rustfs rc get目录
#!/usr/bin/env bash
set -e
echo "🚀 开始执行 rc cp 压测..."

START_TIME=$(date +%s.%N)

# 执行 rc cp 命令 (使用正规参数 --concurrency,包含 -r 递归复制)
rc cp -r --concurrency 64 lvs/benchmark-bucket/tianmu_boto3_raw/ /data1/down/rc_tianmu_boto3_raw/

# 记录高精度结束时间
END_TIME=$(date +%s.%N)

# 计算总耗时 (秒)
ELAPSED=$(awk -v start="${START_TIME}" -v end="${END_TIME}" 'BEGIN { printf "%.2f", end - start }')

# 统计下载后的本地目标目录实际字节数和文件总数
TOTAL_BYTES=$(du -sb /data1/down/rc_tianmu_boto3_raw/ | awk '{print $1}')
TOTAL_FILES=$(find /data1/down/rc_tianmu_boto3_raw/ -type f | wc -l)

# 计算每秒吞吐与速率
BYTES_PER_SEC=$(awk -v b="${TOTAL_BYTES}" -v e="${ELAPSED}" 'BEGIN { printf "%.2f", b / e }')
MBPS=$(awk -v b="${TOTAL_BYTES}" -v e="${ELAPSED}" 'BEGIN { printf "%.2f", (b / 1024 / 1024) / e }')
RPS=$(awk -v f="${TOTAL_FILES}" -v e="${ELAPSED}" 'BEGIN { printf "%.2f", f / e }')
TOTAL_GB=$(awk -v b="${TOTAL_BYTES}" 'BEGIN { printf "%.2f", b / 1024 / 1024 / 1024 }')

echo "============================================================"
echo "🎉 rc cp 下载压测完成!"
echo "⏱️ 总耗时         : ${ELAPSED} 秒"
echo "📂 下载文件总数   : ${TOTAL_FILES} 个"
echo "📦 传输数据总量   : ${TOTAL_BYTES} 字节 (${TOTAL_GB} GB)"
echo "⚡ 每秒传输字节数 : ${BYTES_PER_SEC} Bytes/s"
echo "🚀 平均吞吐速率   : ${MBPS} MB/s"
echo "============================================================"

执行:./benchmark_rc_cp.sh

output...
============================================================
🎉 rc cp 下载压测完成!
⏱️ 总耗时         : 874.47 秒
📂 下载文件总数   : 137891 个
📦 传输数据总量   : 155190886923 字节 (144.53 GB)
⚡ 每秒传输字节数 : 177468508.84 Bytes/s
🚀 平均吞吐速率   : 169.25 MB/s
⚡ 平均 RPS       : 157.69 文件/秒
============================================================

读测试总结

说明: 用python脚本去读上面写进去的数据量,并发开到32个,发现rustfs根本读不出来或很慢,小文件太多了。只有用原生的rc客户端和hdfs客户端来做对比。

cp方式 总耗时 平均吞吐  
HDFS EC 607.93 秒 243.45 MiB/s  
HDFS 三副本 608.23 秒 226.71 MiB/s  
RustFS(rc cp) 874.47 秒 169.25 MB/s  

单个大文件测试

测试描述:针对一个文件的测试比较,用各自的官方提供的客户端进行put get,对比时间,。 每个测试都跑了3次,时间相差不多,随机取的一组测试output。
文件大小:119.47 GiB 环境:客户端环境一致,服务端硬件一致。
磁盘格式:ext4(hdfs) VS XFS(rustfs)

测试脚本示例:

rustfs rc put|get目录(默认收起,点击展开)
#!/usr/bin/env bash
set -e
# nohup ./benchmark_rc_put.sh > benchmark_rc_get.log 2>&1 &

# 文件信息
# 128276262889 Jul 21 18:33 part-00000-beaa4b21-df8d-4d32-8052-5b0e64007522-c000
TOTAL_BYTES=128276262889
TOTAL_FILES=1
# ============================================
echo "🚀 开始执行 rc put | get  测试..."
echo "------------------------------------------------------------"

# 记录高精度开始时间 (秒.纳秒)
START_TIME=$(date +%s.%N)

# 执行 put 命令
#rc put   /data1/test_data/dt=20251019/part-00000-beaa4b21-df8d-4d32-8052-5b0e64007522-c000 lvs/benchmark-bucket/bigfile/
rc get   lvs/benchmark-bucket/bigfile/part-00000-beaa4b21-df8d-4d32-8052-5b0e64007522-c000  /data1/test_data/rc/
# 记录高精度结束时间
END_TIME=$(date +%s.%N)

# 计算总耗时 (秒)
ELAPSED=$(awk -v start="${START_TIME}" -v end="${END_TIME}" 'BEGIN { printf "%.2f", end - start }')

# 计算每秒吞吐与速率
BYTES_PER_SEC=$(awk -v b="${TOTAL_BYTES}" -v e="${ELAPSED}" 'BEGIN { printf "%.2f", b / e }')
MBPS=$(awk -v b="${TOTAL_BYTES}" -v e="${ELAPSED}" 'BEGIN { printf "%.2f", (b / 1024 / 1024) / e }')
RPS=$(awk -v f="${TOTAL_FILES}" -v e="${ELAPSED}" 'BEGIN { printf "%.2f", f / e }')

echo "============================================================"
echo "🎉 done 完成!"
echo "⏱️ 总耗时         : ${ELAPSED} 秒"
echo "📂 文件总数   : 1 个"
echo "⚡ 每秒传输字节数 : ${BYTES_PER_SEC} Bytes/s"
echo "🚀 平均吞吐速率   : ${MBPS} MB/s"
echo "============================================================"

put单个大文件

# hdfs put
============================================================
🎉 hdfs put 完成!
⏱️ 总耗时         : 515.42 秒
📂 下载文件总数   : 1 个
⚡ 每秒传输字节数 : 248877154.34 Bytes/s
🚀 平均吞吐速率   : 237.35 MB/s


# hdfs ec put
🚀 开始执行 hdfs ec put 测试...
------------------------------------------------------------
2026-08-13 14:57:07,213 WARN erasurecode.ErasureCodeNative: ISA-L support is not available in your platform... using builtin-java codec where applicable
============================================================
🎉 hdfs put 完成!
⏱️ 总耗时         : 935.89 秒
📂 下载文件总数   : 1 个
⚡ 每秒传输字节数 : 137063397.29 Bytes/s
🚀 平均吞吐速率   : 130.71 MB/s
⚡ 平均 RPS       : 0.00 文件/秒
============================================================

# rc put
🚀 开始执行 rc put  测试...
------------------------------------------------------------
/data1/test_data/dt=20251019/part-00000-beaa4b21-df8d-4d32-8052-5b0e64007522-c000 -> lvs/benchmark-bucket/bigfile/part-00000-beaa4b21-df8d-4d32-8052-5b0e64007522-c000 (119.47 GiB)
============================================================
🎉 done 完成!
⏱️ 总耗时         : 3955.16 秒
📂 文件总数   : 1 个
⚡ 每秒传输字节数 : 32432635.57 Bytes/s
🚀 平均吞吐速率   : 30.93 MB/s
============================================================

get单个大文件

# hdfs get
🚀 开始执行 hdfs get 测试...
------------------------------------------------------------
============================================================
🎉 hdfs get 完成!
⏱️ 总耗时         : 903.63 秒
📂 下载文件总数   : 1 个
⚡ 每秒传输字节数 : 141956622.61 Bytes/s
🚀 平均吞吐速率   : 135.38 MB/s
============================================================

# hdfs ec get
🚀 开始执行 hdfs  ec get 测试...
------------------------------------------------------------
2026-08-13 15:15:07,427 WARN erasurecode.ErasureCodeNative: ISA-L support is not available in your platform... using builtin-java codec where applicable
============================================================
🎉 hdfs get 完成!
⏱️ 总耗时         : 443.69 秒
📂 下载文件总数   : 1 个
⚡ 每秒传输字节数 : 289112359.73 Bytes/s
🚀 平均吞吐速率   : 275.72 MB/s
⚡ 平均 RPS       : 0.00 文件/秒
============================================================

# rc get
🚀 开始执行 rc get  测试...
------------------------------------------------------------
lvs/benchmark-bucket/bigfile/part-00000-beaa4b21-df8d-4d32-8052-5b0e64007522-c000 -> /data1/test_data/rc/part-00000-beaa4b21-df8d-4d32-8052-5b0e64007522-c000 (119.47 GiB)
============================================================
🎉 done 完成!
⏱️ 总耗时         : 425.51 秒
📂 文件总数   : 1 个
⚡ 每秒传输字节数 : 301464743.22 Bytes/s
🚀 平均吞吐速率   : 287.50 MB/s
============================================================


单个文件测试总结

本次使用同一客户端环境,对一个大小为 128,276,262,889 Bytes(约 119.47 GiB)的大文件分别执行 HDFS 与 RustFS 上传、下载测试。吞吐量按文件有效字节数除以命令墙钟时间计算。

文件上传和下载信息一致

du -sh   /data1/test_data/*/* | head
120G    /data1/test_data/dt=20251019/part-00000-beaa4b21-df8d-4d32-8052-5b0e64007522-c000  
120G    /data1/test_data/ec_hdfs/part-00000-beaa4b21-df8d-4d32-8052-5b0e64007522-c000
120G    /data1/test_data/hdfs/part-00000-beaa4b21-df8d-4d32-8052-5b0e64007522-c000
120G    /data1/test_data/rc/part-00000-beaa4b21-df8d-4d32-8052-5b0e64007522-c000

PUT 对比

存储方式 总耗时 平均吞吐 相对最快
HDFS 三副本 515.42 秒 237.35 MiB/s 1.00 倍
HDFS EC 935.89 秒 130.71 MiB/s 慢 1.82 倍
RustFS(rc put) 3955.16 秒 30.93 MiB/s 慢 7.67 倍

GET 对比

存储方式 总耗时 平均吞吐 相对最快
RustFS(rc get) 425.51 秒 287.50 MiB/s 1.00 倍
HDFS EC 443.69 秒 275.72 MiB/s 慢 1.04 倍
HDFS 三副本 903.63 秒 135.38 MiB/s 慢 2.12 倍

结果分析

  1. 写入:三副本最快,EC 次之,RustFS 明显落后,上面也提到对象存储写数据慢是因为上面忆提过:在生成阶段,S3 协议暂存提交器处于劣势。
  2. 读取:RustFS 与 EC 接近,三副本最慢 。 三副本读取本应无需解码而最快,实测却只有 135.38 MiB/s,仅为 EC 的 49%。这符合 HDFS 连续布局(contiguous)单流读取的特征:客户端顺序读取约:119.47 GiB / 128 MiB ≈ 956 个 Block

三副本是连续布局,一个 Block 完整存在一台 DataNode 的一块盘上。顺序读一个大文件时,同一时刻基本只有一个 Block 在传输。

RustFS 的 287.50 MiB/s 与 EC 基本处于同一水平,仅高 4.3%。RustFS 客户端对大对象可能使用多 Range 并发下载,服务端也从多块盘并行读取分片,这与 EC 的并行读机制类似。是否确实并发下载,需要核对 rc 的并发与分片配置及服务端请求记录。

压测工具数据测试

minIO工具warp压测

minIO官网测试对比HDFS数据 https://www.min.io/blog/hdfsbenchmark
安装warp工具&使用

dpkg -i warp_1.5.0_amd64.deb

warp --version
warp version v1.5.0 - ddb528b


# 配置环境变量
export WARP_ACCESS_KEY_ID=accessrustfsadmin
export WARP_SECRET_ACCESS_KEY=secretrustfsadmin

# mixed混合模式基准测试
warp mixed \
  --host=10.12.74.28 \
  --access-key="$WARP_ACCESS_KEY_ID" \
  --secret-key="$WARP_SECRET_ACCESS_KEY" \
  --obj.size=10MiB \
  --concurrent=32 \
  --duration=5m \
  --bucket=warptest \
  --insecure


#随机文件大小
--obj.randsize
#固定文件大小
--obj.size=10MiB 

=======================--obj.randsize================================	
 λ █████████████████████████████████████████████████████████████████████████ 100%
                                                                          
Reqs: 247242, Errs:0, Objs:247242, Bytes: 261.59GiB                       
 -    DELETE Average: 82 Obj/s; Current 98 Obj/s, 134.6 ms/req            
 -       GET Average: 370 Obj/s, 669.9MiB/s; Current 424 Obj/s, 758.7MiB/s
 -       PUT Average: 123 Obj/s, 222.3MiB/s; Current 140 Obj/s, 228.9MiB/s
 -      STAT Average: 247 Obj/s; Current 283 Obj/s, 11.8 ms/req           
# ... 详细指标省略    
                                                     
=======================obj.obj.size=10MiB================================	
 λ █████████████████████████████████████████████████████████████████████████ 100%
                                                                                                        
Reqs: 117119, Errs:0, Objs:117119, Bytes: 686.00GiB                                                     
 -    DELETE Average: 39 Obj/s; Current 38 Obj/s, 172.0 ms/req                                          
 -       GET Average: 176 Obj/s, 1760.1MiB/s; Current 165 Obj/s, 1653.9MiB/s, 165.1 ms/req, TTFB: 28.9ms
 -       PUT Average: 59 Obj/s, 586.6MiB/s; Current 58 Obj/s, 578.5MiB/s, 479.5 ms/req, TTFB: 337.6ms   
 -      STAT Average: 117 Obj/s; Current 107 Obj/s, 10.8 ms/req                                         
                                                                                                        
# ... 详细指标省略 

HDFS DFSIO压测

############################70000*10##########################################
# mapreduce对小文件处理不是友好的,会根据文件数起map,导致分配或处理过程中timeout,因此海量小文件不适合HFDS,一方面是namenode内存受限,在task处理上小文件过多,也会对计算造成影响。
hadoop jar /usr/local/hadoop-3.2.4/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.2.4-tests.jar TestDFSIO \
  -D mapreduce.map.memory.mb=4096 \
  -D mapreduce.map.java.opts="-Xmx4096m" \
  -D mapreduce.task.io.sort.mb=512 \
  -write -nrFiles 70000 -fileSize 10    -resFile /tmp/readTestDFSIO_10MB_results.log

############################700*1024##########################################
hadoop jar /usr/local/hadoop-3.2.4/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.2.4-tests.jar TestDFSIO \
  -D mapreduce.map.memory.mb=2048 \
  -D mapreduce.map.java.opts="-Xmx2048m" \
  -D mapreduce.task.io.sort.mb=512 \
  -write -nrFiles 700 -fileSize 1024    -resFile /tmp/readTestDFSIO_10MB_results.log

# ... 输出省略 ...
2026-08-11 18:15:27,063 INFO fs.TestDFSIO: ----- TestDFSIO ----- : write
2026-08-11 18:15:27,063 INFO fs.TestDFSIO:             Date & time: Tue Aug 11 18:15:27 CST 2026
2026-08-11 18:15:27,063 INFO fs.TestDFSIO:         Number of files: 700
2026-08-11 18:15:27,063 INFO fs.TestDFSIO:  Total MBytes processed: 716800
2026-08-11 18:15:27,063 INFO fs.TestDFSIO:       Throughput mb/sec: 35.13
2026-08-11 18:15:27,063 INFO fs.TestDFSIO:  Average IO rate mb/sec: 52.49
2026-08-11 18:15:27,063 INFO fs.TestDFSIO:   IO rate std deviation: 39.55
2026-08-11 18:15:27,063 INFO fs.TestDFSIO:      Test exec time sec: 808.31
2026-08-11 18:15:27,063 INFO fs.TestDFSIO: 

# 糊略计算总体的Throughput,上面的Throughput mb/sec: 35.13是单个map的吞吐量。
# 因为dfsio测试模型和warp测试模型不同,不能直接对比。但可以估算大致的吞吐量:
716800 MiB / 808.31 s ≈ 886.8 MiB/s
############################70*10240##########################################
hadoop jar /usr/local/hadoop-3.2.4/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.2.4-tests.jar TestDFSIO \
  -D mapreduce.map.memory.mb=4096 \
  -D mapreduce.map.java.opts="-Xmx4096m" \
  -D mapreduce.task.io.sort.mb=512 \
  -write -nrFiles 70 -fileSize 10240    -resFile /tmp/readTestDFSIO_10MB_results.log

# ... 输出省略 ...
2026-08-11 18:36:39,573 INFO fs.TestDFSIO: ----- TestDFSIO ----- : write
2026-08-11 18:36:39,573 INFO fs.TestDFSIO:             Date & time: Tue Aug 11 18:36:39 CST 2026
2026-08-11 18:36:39,573 INFO fs.TestDFSIO:         Number of files: 70
2026-08-11 18:36:39,573 INFO fs.TestDFSIO:  Total MBytes processed: 716800
2026-08-11 18:36:39,573 INFO fs.TestDFSIO:       Throughput mb/sec: 34.71
2026-08-11 18:36:39,573 INFO fs.TestDFSIO:  Average IO rate mb/sec: 35.58
2026-08-11 18:36:39,573 INFO fs.TestDFSIO:   IO rate std deviation: 6.24
2026-08-11 18:36:39,573 INFO fs.TestDFSIO:      Test exec time sec: 704.99

# 因为dfsio测试模型和warp测试模型不同,不能直接对比。但可以估算大致的吞吐量:
# 总体计算 Average IO rate mb/sec,因为70个是并行的
Throughput mb/sec:=(35.58*70)=2490.6
# 糊略计算总体的Throughput mb/sec,这里的时间包含map起动时间和app起动时间,关闭时间。
716800/704.99≈1007.8MiB/s


# 读测试
hadoop jar /usr/local/hadoop-3.2.4/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.2.4-tests.jar TestDFSIO \
  -D mapreduce.map.memory.mb=4096 \
  -D mapreduce.map.java.opts="-Xmx4096m" \
  -D mapreduce.task.io.sort.mb=512 \
  -read -nrFiles 70 -fileSize 10240 -resFile /tmp/wirteTestDFSIO_10MB_results.log
  ...
2026-08-12 10:27:06,963 INFO fs.TestDFSIO: ----- TestDFSIO ----- : read
2026-08-12 10:27:06,963 INFO fs.TestDFSIO:             Date & time: Wed Aug 12 10:27:06 CST 2026
2026-08-12 10:27:06,963 INFO fs.TestDFSIO:         Number of files: 70
2026-08-12 10:27:06,963 INFO fs.TestDFSIO:  Total MBytes processed: 716800
2026-08-12 10:27:06,964 INFO fs.TestDFSIO:       Throughput mb/sec: 25.5
2026-08-12 10:27:06,964 INFO fs.TestDFSIO:  Average IO rate mb/sec: 27.14
2026-08-12 10:27:06,964 INFO fs.TestDFSIO:   IO rate std deviation: 6.49
2026-08-12 10:27:06,964 INFO fs.TestDFSIO:      Test exec time sec: 982.15

# 总体计算 Average IO rate mb/sec,因为70个是并行的
Throughput mb/sec:=(27.14*70)≈1899.8MiB/s
# 糊略计算总体的Throughput mb/sec,这里的时间包含map起动时间和app起动时间,关闭时间。
716800/982.15≈729.82MiB/s

工具压测结论

本次对比可以作为 RustFS S3 与 HDFS 存储吞吐的粗略参考,但不能视为严格的同条件基准测试,原因是两边的压测模型不同:

RustFS 使用 Warp mixed,PUT、GET、STAT、DELETE 混合执行。
HDFS 使用 TestDFSIO,写入和读取分成两个独立 Job。
Warp 的 –concurrent=32 是所有操作共享的总并发,不是 32 个 PUT 加 32 个 GET。
TestDFSIO 使用 MapReduce,一个文件对应一个 Map Task;70 个文件大约对应 70 个 Map,但实际运行并发受 YARN 资源限制。
Warp 固定运行 5 分钟;TestDFSIO 按任务完成时间结束,包含 Job、Application、Map 启动和回收开销。

核心指标对比

系统和模式 操作 数据规模 统计吞吐 按总数据量/Job时间计算
RustFS Warp 固定大小 PUT 约 172GiB 586.6MiB/s 约 586.6MiB/s
RustFS Warp 固定大小 GET 约 516GiB 1760.1MiB/s 约 1760.1MiB/s
RustFS Warp 固定大小 PUT+GET 686GiB 2346.7MiB/s 约 2346.7MiB/s
RustFS Warp 随机大小 PUT 约 65GiB 222.3MiB/s 约 222.3MiB/s
RustFS Warp 随机大小 GET 约 196GiB 669.9MiB/s 约 669.9MiB/s
RustFS Warp 随机大小 PUT+GET 261.59GiB 892.2MiB/s 约 892.2MiB/s
HDFS TestDFSIO,700×1GiB WRITE 700GiB 35.13MiB/s* 886.8MiB/s
HDFS TestDFSIO,70×10GiB WRITE 700GiB 34.71MiB/s* 1007.8MiB/s
HDFS TestDFSIO,70×10GiB READ 700GiB 25.50MiB/s* 729.8MiB/s

粗略对比

在本次测试条件下:

因此只能得出以下粗略结论:
1.在当前测试条件下,HDFS 的独立写入 Job 墙钟吞吐高于 Warp mixed 模式中的 PUT 吞吐。
2.RustFS Warp 固定大小模式的 GET 吞吐高于 HDFS TestDFSIO 读取 Job 的墙钟吞吐。
3.Warp 随机大小模式的 PUT/GET 吞吐均低于固定 10MiB 模式,说明对象大小分布和请求数对结果影响明显。
4.HDFS 的 Map 内部估算吞吐与 Warp 的单项吞吐接近,但考虑 Job 启动、调度和收尾后,整体墙钟吞吐会明显降低。
5.当前结果适合做工程上的量级参考,不适合据此直接得出 RustFS 与 HDFS 的绝对性能排名。

大纲: