当前位置: 首页 > news >正文

【架构实战】对象存储架构:从NAS到OSS的演进

一、NAS存储把我们逼上了绝路

2019年,我们的文件存储用的是NAS(网络附加存储),所有服务器挂载同一个NAS目录。

一开始好好的,但随着文件越来越多,问题开始出现:

  1. NAS单点故障——NAS宕机,所有文件读写失败
  2. 磁盘IO瓶颈——并发上传时NAS扛不住
  3. 扩容困难——NAS容量固定,扩容要停机
  4. CDN无法对接——NAS文件无法直接用CDN加速

后来我们迁移到了对象存储(MinIO/OSS),所有问题迎刃而解。


二、对象存储架构

2.1 对比

┌─────────────────────────────────────────────────────────────────┐ │ 文件存储方案对比 │ │ │ │ 方案 │ 可靠性 │ 性能 │ 扩展性 │ 成本 │ CDN │ │ ───────────────────────────────────────────────────────────── │ │ 本地磁盘 │ 低 │ 高 │ 差 │ 低 │ 不支持 │ │ NAS │ 中 │ 中 │ 差 │ 高 │ 不支持 │ │ MinIO │ 高 │ 高 │ 好 │ 低 │ 支持 │ │ 阿里OSS │ 极高 │ 高 │ 极好 │ 中 │ 原生支持 │ │ AWS S3 │ 极高 │ 高 │ 极好 │ 中 │ 原生支持 │ │ │ └──────────────────────────────────────────────────────────────────┘

三、MinIO实践

3.1 部署

# docker-compose.yml - MinIO集群version:'3'services:minio1:image:minio/minioports:-"9000:9000"-"9001:9001"environment:MINIO_ROOT_USER:adminMINIO_ROOT_PASSWORD:admin123456command:server http://minio{1...4}/data--console-address ":9001"volumes:-./data1:/dataminio2:image:minio/miniocommand:server http://minio{1...4}/data--console-address ":9001"volumes:-./data2:/dataminio3:image:minio/miniocommand:server http://minio{1...4}/data--console-address ":9001"volumes:-./data3:/dataminio4:image:minio/miniocommand:server http://minio{1...4}/data--console-address ":9001"volumes:-./data4:/data

3.2 Java客户端

/** * MinIO文件服务 */@Service@Slf4jpublicclassMinioFileService{@AutowiredprivateMinioClientminioClient;/** * 上传文件 */publicFileUploadResultupload(Stringbucket,StringobjectKey,InputStreamstream,longsize,StringcontentType){try{minioClient.putObject(PutObjectArgs.builder().bucket(bucket).object(objectKey).stream(stream,size,-1).contentType(contentType).build());Stringurl=getFileUrl(bucket,objectKey);returnFileUploadResult.success(objectKey,url);}catch(Exceptione){log.error("文件上传失败: bucket={}, key={}",bucket,objectKey,e);thrownewBusinessException("文件上传失败");}}/** * 下载文件 */publicInputStreamdownload(Stringbucket,StringobjectKey){try{returnminioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(objectKey).build());}catch(Exceptione){log.error("文件下载失败: bucket={}, key={}",bucket,objectKey,e);thrownewBusinessException("文件下载失败");}}/** * 生成预签名URL(临时访问) */publicStringgetPresignedUrl(Stringbucket,StringobjectKey,intexpireMinutes){try{returnminioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucket).object(objectKey).method(Method.GET).expiry(expireMinutes,TimeUnit.MINUTES).build());}catch(Exceptione){log.error("生成预签名URL失败",e);thrownewBusinessException("生成文件访问链接失败");}}/** * 删除文件 */publicvoiddelete(Stringbucket,StringobjectKey){try{minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(objectKey).build());log.info("文件删除成功: bucket={}, key={}",bucket,objectKey);}catch(Exceptione){log.error("文件删除失败",e);}}}

四、文件管理架构

4.1 文件元数据管理

CREATETABLEfile_info(idBIGINTPRIMARYKEYAUTO_INCREMENT,file_nameVARCHAR(255)NOTNULL,file_typeVARCHAR(50),file_sizeBIGINT,file_md5VARCHAR(32),object_keyVARCHAR(500)NOTNULL,bucketVARCHAR(100)NOTNULL,content_typeVARCHAR(100),business_typeVARCHAR(50),-- avatar/product/documentbusiness_idVARCHAR(100),-- 关联业务IDuser_idBIGINT,create_timeDATETIME,is_deletedTINYINTDEFAULT0,INDEXidx_md5(file_md5),INDEXidx_business(business_type,business_id),INDEXidx_user(user_id));

4.2 文件服务

/** * 文件管理服务 */@Service@Slf4jpublicclassFileManagementService{@AutowiredprivateMinioFileServiceminioService;@AutowiredprivateFileInfoMapperfileInfoMapper;/** * 上传文件(完整流程) */@TransactionalpublicFileUploadResultuploadFile(MultipartFilefile,StringbusinessType,StringbusinessId,LonguserId){// 1. 计算MD5(秒传检查)Stringmd5=calculateMd5(file);FileInfoexisting=fileInfoMapper.selectByMd5(md5);if(existing!=null){// 秒传:创建新引用FileInfonewFile=FileInfo.builder().fileName(file.getOriginalFilename()).fileType(getFileExtension(file.getOriginalFilename())).fileSize(file.getSize()).fileMd5(md5).objectKey(existing.getObjectKey()).bucket(existing.getBucket()).businessType(businessType).businessId(businessId).userId(userId).build();fileInfoMapper.insert(newFile);returnFileUploadResult.quickUpload(newFile.getFileUrl());}// 2. 生成objectKeyStringobjectKey=generateObjectKey(businessType,file.getOriginalFilename());Stringbucket=getBucket(businessType);// 3. 上传到MinIOtry{FileUploadResultresult=minioService.upload(bucket,objectKey,file.getInputStream(),file.getSize(),file.getContentType());// 4. 保存文件信息FileInfofileInfo=FileInfo.builder().fileName(file.getOriginalFilename()).fileType(getFileExtension(file.getOriginalFilename())).fileSize(file.getSize()).fileMd5(md5).objectKey(objectKey).bucket(bucket).contentType(file.getContentType()).businessType(businessType).businessId(businessId).userId(userId).build();fileInfoMapper.insert(fileInfo);returnresult;}catch(IOExceptione){log.error("文件上传异常",e);thrownewBusinessException("文件上传失败");}}}

五、踩坑实录

坑1:Bucket没有设置访问策略

上传的文件无法通过URL直接访问。

解决:设置Bucket为公共读或使用预签名URL。

坑2:文件名中文乱码

上传中文文件名的文件,下载时文件名乱码。

解决:URL编码文件名,设置Content-Disposition。

坑3:存储成本失控

大量过期文件没有清理,存储费用持续增长。

解决:设置文件生命周期,定期清理过期文件。

坑4:图片没有压缩

直接上传原图,5MB的图片加载很慢。

解决:上传时自动压缩,或使用图片处理服务。

坑5:没有防盗链

文件被其他网站直接引用,流量费用暴增。

解决:设置Referer白名单或使用签名URL。


六、总结

对象存储最佳实践:

原则说明
私有Bucket默认私有,需要签名才能访问
预签名URL临时授权访问
秒传MD5去重
生命周期自动清理过期文件
图片处理自动压缩、缩略图
防盗链Referer白名单

血的教训:

文件存储不是"能存就行"。安全、成本、性能,每一个都要考虑清楚。

思考题:你的系统用了什么文件存储方案?


个人观点,仅供参考

http://www.jsqmd.com/news/977950/

相关文章:

  • 3分钟搞定XAPK转APK:这款无依赖Python工具让你告别安装烦恼
  • 2026四川风幕机厂家评测:5家靠谱品牌工况实测对比 - 优质品牌商家
  • 赤火时代水淬炉,好用又靠谱,性价比超高 - 工业品牌热点
  • C++继承与多态进阶实战指南
  • Redis在后端缓存设计中的最佳实践:提升系统响应速度
  • 2026年口碑好的高师傅漏水检测机构推荐 - mypinpai
  • 保姆级避坑指南:用FNL数据从WPS到WRF再到ARWpost的完整流程(附namelist.input配置)
  • 原神帧率解锁完整指南:5步实现144帧极致流畅体验
  • 从零到提交第一个漏洞,你需要系统做对哪6步?
  • 2026年推荐餐椅制造商哪家好 - mypinpai
  • 深度解析UABEA:现代Unity游戏资源编辑与模组开发实战指南
  • 时事蹭热度系列之四:那个哭着返校的女孩,让我重新思考了教育
  • 今日开源[第12期]LiteParse - zhang
  • 网盘直链解析技术实践:LinkSwift 开源项目深度解析
  • 选购空调家电制冷产品回收加工厂的要点 - 工业品牌热点
  • SpringBoot自动配置原理深度解析
  • 从黑屏到流畅:在云服务器(AWS EC2 / 腾讯云CVM)上为Ubuntu配置xrdp远程桌面的实战记录
  • 工业水处理选购,嘉佰晟环境好不好? - mypinpai
  • 信号处理实战:用db4小波四层分解,从Matlab分析到C语言移植的避坑指南
  • 保姆级教程:新版Dubbo-Admin在Windows和Linux上的完整安装与配置(含常见打包报错解决方案)
  • 2026年成都风幕机厂家排行:餐饮店风幕机/厂房通风离心风机/商用厨房排烟离心风机/多场景适配实力盘点 - 优质品牌商家
  • Kotlin 开发 - Kotlin 反引号转义关键字
  • 如何快速部署网易云音乐插件管理器:5个专业优化策略指南
  • 有资质的建筑垃圾清运,苏园再生 - 工业品牌热点
  • STM32 PID温度控制系统:如何实现工业级±0.5℃精度控制
  • 锦绣御景花卉的花卉培育周期长吗 - mypinpai
  • MATLAB R2021b + UE4.25联合仿真避坑实录:手把手解决插件路径找不到的报错
  • 鸿蒙原生 ArkTS:border 的盒模型、深层嵌套约束传递与 scale 缩放
  • OriginPro 2021b保姆级教程:搞定科研论文里的多组数据填充面积图(附数据排列避坑指南)
  • 如何快速解锁网易云音乐:终极NCM文件转换完整指南