文章目录
- [NAS + 对象存储 + LMDB/HDF5:海量小文件存储最佳实践](#NAS + 对象存储 + LMDB/HDF5:海量小文件存储最佳实践)
- [一、传统 NAS 为什么越来越慢?](#一、传统 NAS 为什么越来越慢?)
- 二、优化目标
- 三、总体架构
- [四、第一层:NAS 负责容量存储](#四、第一层:NAS 负责容量存储)
- [五、第二层:LMDB/HDF5 负责打包](#五、第二层:LMDB/HDF5 负责打包)
- 六、第三层:元数据数据库
- 七、第四层:对象存储
- [八、AI 训练如何读取?](#八、AI 训练如何读取?)
- 九、目录组织建议
- 十、数据生命周期管理
- 十一、性能对比
- 十二、适用场景
- 总结
NAS + 对象存储 + LMDB/HDF5:海量小文件存储最佳实践
前言
随着 AI、工业视觉、自动驾驶、卫星遥感等行业的发展,企业每天都会产生海量图片、点云、日志、视频切片等数据。
例如一个工业 AI 项目:
text
100 台相机
每秒采集 20 张图片
每天运行 24 小时
≈ 1.7 亿张图片/月
如果每张图片大小仅为 50KB,直接存储到 NAS 中,很快就会遇到以下问题:
- 文件数量达到数千万甚至数亿
- 文件系统 inode 耗尽
ls、find、open()操作越来越慢- AI 训练读取速度越来越低
- 数据备份时间成倍增长
因此,仅依赖 NAS 已无法满足海量小文件存储需求。
本文介绍一套企业级存储架构,能够很好地解决这一问题。
一、传统 NAS 为什么越来越慢?
假设有:
text
5000 万张图片
0000001.jpg
0000002.jpg
0000003.jpg
......
50000000.jpg
AI 训练时:
cpp
for(image : images)
{
open(image);
read(image);
close(image);
}
对于每张图片,操作系统都需要:
text
Lookup inode
↓
打开文件(Open)
↓
读取数据(Read)
↓
关闭文件(Close)
如果有:
text
5000 万文件
意味着:
text
5000 万次
open()
5000 万次
close()
真正读取的数据只有几十 KB,而大量时间消耗在元数据管理上。
二、优化目标
我们希望达到以下目标:
- 支持 亿级文件管理
- AI 训练能够顺序读取数据
- 减少元数据访问
- 支持随机读取指定图片
- 支持断点恢复
- 支持在线扩容
- 与 NAS 兼容
三、总体架构
推荐采用如下架构:
text
数据采集服务器
│
▼
数据预处理程序
│
┌───────────────┴──────────────┐
▼ ▼
LMDB/HDF5 打包 Metadata数据库
│ │
│ 图片属性
│ 时间
│ 标签
│ Offset
▼ │
NAS / 对象存储(MinIO、Ceph)
│
┌───────────────┴──────────────┐
▼ ▼
AI训练 查询系统
整个系统分为四层。
四、第一层:NAS 负责容量存储
NAS 不再保存:
text
image1.jpg
image2.jpg
......
而是保存:
text
dataset001.lmdb
dataset002.lmdb
dataset003.lmdb
例如:
text
NAS
├── Dataset
│ ├── 20260801.lmdb
│ ├── 20260802.lmdb
│ ├── 20260803.lmdb
│ └── ...
这样:
原来:
text
1000万文件
变成:
text
100个文件
NAS 的性能立即提升。
五、第二层:LMDB/HDF5 负责打包
例如:
一天采集:
text
100000 张图片
不要:
text
100000 jpg
而是:
text
20260801.lmdb
里面:
text
key
↓
图片编号
↓
value
↓
JPEG二进制
例如:
text
0000001
↓
JPEG
读取:
cpp
txn.get("0000001");
不用:
cpp
fopen("0000001.jpg");
速度通常快数倍以上。
如果使用 HDF5:
text
dataset.h5
├── images
├── labels
├── timestamp
└── gps
一个文件即可管理几十万张图片及其相关数据。
六、第三层:元数据数据库
数据库中只保存图片的信息,而不是图片本身。
例如:
| ImageID | Container | Key | Time | Label |
|---|---|---|---|---|
| 10001 | 20260801.lmdb | 000001 | 10:20:31 | OK |
| 10002 | 20260801.lmdb | 000002 | 10:20:32 | NG |
| 10003 | 20260801.lmdb | 000003 | 10:20:33 | OK |
数据库推荐:
- SQLite(单机)
- MySQL(中小规模)
- PostgreSQL(推荐)
- RocksDB(高性能 KV)
- Redis(缓存热点数据)
查询流程:
text
ImageID
↓
数据库
↓
Container
↓
LMDB
↓
图片
无需扫描目录。
七、第四层:对象存储
如果数据继续增长:
例如:
text
50TB
100TB
500TB
建议把:
text
LMDB
HDF5
直接存入对象存储。
例如:
text
MinIO
↓
bucket
↓
2026/
↓
08/
↓
dataset001.lmdb
对象存储负责:
- 副本管理
- 自动扩容
- 数据校验
- 生命周期管理
NAS 则作为高速缓存或共享存储。
八、AI 训练如何读取?
传统方式:
cpp
for(file : images)
{
cv::imread(file);
}
新的方式:
cpp
LMDB
↓
Cursor
↓
Next()
↓
Decode JPEG
↓
Tensor
优势:
- 顺序读取
- 少量系统调用
- 几乎没有目录遍历
- SSD 连续读性能充分发挥
在深度学习框架中(PyTorch、TensorFlow),LMDB、WebDataset、TFRecord 等格式都能显著提升训练吞吐量。
九、目录组织建议
建议按时间和业务维度组织数据:
text
Dataset/
├── Camera01
│ ├── 20260801.lmdb
│ ├── 20260802.lmdb
│ └── ...
│
├── Camera02
│ ├── 20260801.lmdb
│ └── ...
│
└── Metadata
├── image.db
└── label.db
避免单个目录存放数百万文件。
十、数据生命周期管理
不同阶段采用不同存储介质:
text
最近7天
│
▼
NVMe SSD(高速训练)
最近3个月
│
▼
NAS
半年以上
│
▼
MinIO/Ceph
历史归档
│
▼
磁带库或冷存储
这样既保证性能,又降低存储成本。
十一、性能对比
以 1000 万张图片(50KB/张) 为例:
| 指标 | 传统 NAS | NAS + LMDB |
|---|---|---|
| 文件数量 | 1000 万 | 100 个左右 |
open()/close() |
1000 万次 | 100 次左右 |
| 元数据访问 | 极高 | 极低 |
| AI 训练读取 | 随机 IO | 顺序 IO |
| NAS 压力 | 很高 | 很低 |
| 扩容难度 | 高 | 低 |
| 数据管理 | 困难 | 简单 |
十二、适用场景
该方案适用于:
- 工业视觉检测(AOI、AVI)
- 自动驾驶数据采集
- 医学影像(CT、MRI)
- 卫星遥感
- 视频智能分析
- 无人机巡检
- AI 大模型训练数据管理
- 海量日志与传感器数据存储
总结
对于海量小文件,NAS 不应作为最终的小文件管理系统,而应作为高可靠的底层存储平台。推荐采用以下分层架构:
text
数据采集
│
▼
LMDB / HDF5 / WebDataset 打包
│
▼
元数据数据库(PostgreSQL / RocksDB)
│
▼
NAS(共享存储)
│
▼
MinIO / Ceph(对象存储)
│
▼
AI 训练、查询分析、业务系统
该方案将大量零散的小文件聚合为少量高效的数据容器,通过数据库维护元数据,再利用 NAS 和对象存储提供可靠的数据承载与扩展能力。相比直接将数千万图片存放在 NAS 中,这种架构能够显著降低元数据开销、提升 AI 训练吞吐量,并使系统更容易扩容和维护,是目前工业 AI、自动驾驶、遥感和医疗影像等领域广泛采用的工程实践。