news 2026/9/17 11:30:11

system-design-notes:邮件附件如何存储?本地目录存储的目录结构设计详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
system-design-notes:邮件附件如何存储?本地目录存储的目录结构设计详解

system-design-notes:邮件附件如何存储?本地目录存储的目录结构设计详解

【免费下载链接】system-design-notesNotes of the book System Desgin Interview - An Insider's Guide项目地址: https://gitcode.com/GitHub_Trending/sy/system-design-notes

📬system-design-notes是经典系统设计面试书籍System Design Interview: An Insider's Guide的配套笔记仓库,用 28 个章节拆解了从缓存扩展到分布式邮件服务的真实系统设计。本章聚焦第 23 章 Distributed Email Service——设计一个类似 Gmail 的分布式邮件系统,其中「邮件附件如何存储」是一个极具代表性的问题:从小型邮件服务器上的本地目录存储(Maildir 目录结构设计),到大规模场景下附件分离到对象存储的演进,本文带你一次看懂。

一、传统邮件服务器:一封邮件就是一个文件

在设计分布式方案之前,先理解传统邮件服务器是怎么存邮件的。传统架构下,Alice 通过 SMTP 发送邮件,Outlook 服务器查询 DNS 的 MX 记录,把邮件投递给 Gmail 服务器,Bob 再通过 IMAP/POP 拉取邮件:

![传统邮件服务器与本地文件存储架构](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/23. Distributed Email Service/images/traditional-mail-server.png?utm_source=gitcode_repo_files)

这种模式在用户量有限时运行良好。其核心存储策略非常朴素:每封邮件都是本地文件系统上的一个独立文件。当规模增长后,磁盘 I/O 会成为瓶颈,而且单台服务器一旦宕机,所有数据都面临丢失风险,无法满足高可用与可靠性要求。

二、Maildir 目录结构设计详解

那么,"每封邮件一个文件"具体是怎么组织目录的?业界事实标准是Maildir 方案。仓库中的这张图展示了它的典型结构:

![Maildir 本地目录存储结构设计,每封邮件是独立文件](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/23. Distributed Email Service/images/local-dir-storage.png?utm_source=gitcode_repo_files)

以 Linux 为例,目录结构如下:

home/ ├── user1/ │ └── Maildir/ │ ├── cur/ # 当前(已读/处理中)的邮件 │ ├── new/ # 新到达、尚未处理的邮件 │ └── tmp/ # 正在写入中的临时邮件 └── user2/ └── Maildir/ ├── cur/ ├── new/ └── tmp/

1️⃣ cur、new、tmp 三个子目录各自做什么?

目录作用
new新邮件先落在这里。邮箱程序轮询时从这里发现新邮件,读完后文件被移走
cur表示"当前已交付"的邮件集合。客户端正在查看、已标记已读或保留的邮件都在这里
tmp临时缓冲区。新邮件先完整写入 tmp,写完后通过一次原子 rename 移动到 new

2️⃣ 为什么 tmp 目录是 Maildir 的精髓?

🔑原子性写入:邮件写入过程分两步——先写入tmp,再用rename()原子地移动到new。rename 在同一文件系统上是原子操作,因此任何读者看到的要么是完整的旧状态,要么是完整的邮件,永远不会读到"写了一半"的邮件。

🔑无锁设计:所有状态变化都通过"文件在目录间移动"来表达,目录里有什么文件就是什么状态。不需要文件锁,天然支持多进程并发读。

🔑故障恢复简单:如果服务器在写入时崩溃,tmp中残留的不完整文件在重启时直接清理即可,不会污染newcur

三、本地目录存储在大规模下的瓶颈

回到 README.md 中的估算:10 亿用户、每天 1000 万封邮件、约 20% 邮件带平均 500KB 的附件,一年附件存储量就达 1460PB——本地磁盘目录显然撑不住。本地目录存储的三大硬伤:

  • ⚠️磁盘 I/O 瓶颈:海量小文件随机读写,IOPS 吃紧
  • ⚠️单点故障:磁盘损坏或服务器宕机即数据丢失
  • ⚠️无法水平扩展:本地目录天然绑定单台服务器

四、演进方向:附件元数据与文件本体分离

所以分布式邮件服务中,附件存储发生了关键转变——元数据进数据库,文件本体进对象存储(如 Amazon S3):

![分布式邮件系统高层架构,附件存储在对象存储](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/23. Distributed Email Service/images/high-level-architecture.png?utm_source=gitcode_repo_files)

数据库中的附件表非常精简,只保存文件名对象存储 URL的映射,而不是附件本体:

![邮件附件表结构设计,attachments 表按 email_id 与 filename 索引](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/23. Distributed Email Service/images/attachments.png?utm_source=gitcode_repo_files)

字段说明
emails_by_userattachments:LIST<filename\|size>邮件行内只记录附件的文件名和大小
attachmentsemail_id (TIMEUUID), filename (K), url文件名作为主键,url 指向对象存储

这样设计的好处:

  • ✅ 读取邮件列表时不触碰大文件,列表页响应快
  • ✅ 附件按需从对象存储拉取,可独立水平扩展
  • ✅ 补充细节:邮件附件在网络传输时是base64 编码的,主流邮件服务通常限制在 25MB 以内

💡 进阶优化点(可作为系统设计加分项):去重。不同的用户多次发送同一个附件时,可以用内容哈希(如 SHA-256)判重,只存储一份,进一步节省对象存储成本。

五、小结:一张图看懂附件存储的演进

阶段附件存储位置代表适合规模
传统方案本地 Maildir 目录(new/cur/tmp)传统邮件服务器单服务器、有限用户
分布式方案元数据在数据库 + 本体在对象存储(S3)Gmail 类系统十亿级用户、PB 级存储

Maildir 的 cur/new/tmp 三目录结构是单机邮件存储的经典设计,理解它的原子写入和无锁思想;而附件与元数据分离、大文件下沉对象存储,则是任何大规模文件/附件类系统(网盘、云相册)通用的设计模式。想深入完整的设计推导(发送流程、收件流程、搜索、多数据中心容灾),可阅读 23. Distributed Email Service 章节 原文。

【免费下载链接】system-design-notesNotes of the book System Desgin Interview - An Insider's Guide项目地址: https://gitcode.com/GitHub_Trending/sy/system-design-notes

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 11:28:18

OCA认证模拟题15解析:SQL查询过滤与连接考点复盘

简介&#xff1a;OCA认证分类模拟题15是一份面向Oracle认证助理&#xff08;OCA&#xff09;备考者的配套练习文档&#xff0c;聚焦数据库升级、数据泵&#xff08;Data Pump&#xff09;迁移、空间管理、组件兼容性等核心模块&#xff0c;适合正在系统准备OCA考试&#xff0c;…

作者头像 李华
网站建设 2026/9/17 11:22:06

进口编码器停产后怎么替代?机械电气协议参数四层匹配与三条路径

周三凌晨两点&#xff0c;产线维护群里弹出一条消息&#xff1a;三号机送料轴报编码器故障&#xff0c;换上去的备用件开始跳数。翻出原件型号去官网一查&#xff0c;停产已经五年&#xff0c;最后一批库存也被同行扫走了。这种场面在设备圈太常见了——编码器这种看着不起眼的…

作者头像 李华
网站建设 2026/9/17 11:20:52

ZeroClaw执行机制解析:WASM沙箱、DynamicExec与lced协同原理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 11:20:50

DMA完成通知机制:从MSI-X、完成队列到高性能轮询

1. "我干完了"这句话&#xff0c;硬件到底是用什么方式说出来的写驱动或者调 AI 推理服务的时候&#xff0c;最容易被忽略的一环&#xff0c;恰恰是数据搬完之后那句"我干完了"。DMA 把一块 buffer 从网卡搬到内存、从 SSD 搬到主机、从主机搬到 GPU 显存&…

作者头像 李华
网站建设 2026/9/17 11:17:32

制造业知识图谱实战:从工业相机丢帧到语义互联

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华