system-design-notes第23章:设计分布式邮件服务(类Gmail)完整指南
2026/9/17 14:28:46 网站建设 项目流程

system-design-notes第23章:设计分布式邮件服务(类Gmail)完整指南

【免费下载链接】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第23章,带你完整走一遍如何设计一个类似Gmail分布式邮件服务:从容量估算、高层架构、邮件收发流程,到元数据数据库与全文搜索,帮助新手快速掌握这个经典的系统设计面试题。

📌 为什么设计"类Gmail"邮件系统

2020年 Gmail 拥有18亿活跃用户,Outlook 也有4亿用户,邮件服务是典型的大规模分布式系统设计问题。

核心功能(设计范围):

  • 📤 发送邮件 / 📥 接收邮件
  • 📂 获取与过滤邮件
  • 🔍 全文搜索
  • 🛡️ 反垃圾邮件保护

非功能性需求

需求关键点
可靠性绝不能丢失用户的邮件
可用性用复制防止单点故障,容忍部分失败
可扩展性用户增长时系统能弹性扩展
灵活性选用 HTTP 而非 SMTP 等协议,便于扩展新功能

🎯 第1步:明确需求与容量估算

开始设计前,先做背级估算(back-of-the-envelope estimation):

指标假设估算结果
用户规模10亿用户
发送负载每人每天发 10 封10万封/秒
元数据存储每人每天收 40 封,每封约 50KB 元数据730PB/年
附件20% 的邮件带 500KB 附件1460PB/年

💡 存储量每年超过 2PB,这正是"必须分布式"的根本原因。估算方法可参考 02. Back Of the Envelope Estimation。

🏗️ 第2步:高层架构设计

从传统邮件服务器说起

先看传统邮件服务器如何工作:Alice 通过SMTP把邮件发到 Outlook 服务器,Outlook 服务器查询DNS 的 MX 记录找到 Gmail 的邮件服务器并转发,Bob 再用IMAP/POP从本地服务器取信。

![传统邮件服务器架构图:SMTP发送、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)

跨服务器发邮件前需先查询 MX 记录,以下是查询 gmail.com 的 MX 记录示例:

![通过nslookup查询Gmail邮件交换器MX记录的终端示例](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/23. Distributed Email Service/images/dns-lookup.png?utm_source=gitcode_repo_files)

传统架构中,每封邮件都是本地文件系统里的一个独立文件:

![传统邮件在本地目录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)

随着规模增长,磁盘 I/O 成为瓶颈,且磁盘损坏、服务器宕机无法满足高可用与可靠性要求。

分布式邮件服务器架构

分布式邮件服务器仍支持 IMAP/POP 与 SMTP,但对 Web 端则提供HTTP 上的 RESTful API

  • POST /v1/messages— 发送邮件(To、Cc、Bcc)
  • GET /v1/folders— 获取全部文件夹
  • GET /v1/folders/{:folder_id}/messages— 分页获取文件夹中的邮件
  • GET /v1/messages/{:message_id}— 获取邮件详情

高层架构包含 7 个部分:

![分布式邮件服务高层架构:Webmail、Web服务器、实时服务器与存储层(元数据、附件、缓存、搜索)](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/19. Distributed Message Queue/images/high-level-architecture.png?utm_source=gitcode_repo_files)

组件职责
Webmail用户通过浏览器收发邮件
Web Servers登录、注册、用户画像等对外服务
Real-time Servers实时推送新邮件(WebSocket,旧浏览器降级长轮询)
Metadata DB主题、正文、发件人等元数据
Attachment Store对象存储(类 S3),存大文件
Distributed Cache用 Redis 缓存最近邮件,改善体验
Search Store分布式文档库,支持全文搜索

设计原文见 Step 2: High-Level Design。

💌 邮件发送流程详解

![分布式邮件服务发送流程图:负载均衡、消息队列、SMTP出站工作器与存储层协同](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/23. Distributed Email Service/images/email-sending-flow.png?utm_source=gitcode_repo_files)

流程拆解:

  1. 用户写完邮件点"发送",请求先到负载均衡器
  2. 负载均衡器做限流,再路由到某台 Web 服务器
  3. Web 服务器做基础校验(如邮件大小);发件人与收件人同域则短路直发(先做垃圾检查)
  4. 校验通过 → 写入发送消息队列(附件只引用对象存储的地址);失败 → 进错误队列
  5. SMTP 出站工作器拉取消息,执行垃圾邮件/病毒检查后路由到目标邮件服务器
  6. 邮件存入"已发送"文件夹

⚠️ 务必监控发送队列长度:接收方服务器不可用时用指数退避重试;消费者吞吐不足时扩容消费者。

📥 邮件接收流程详解

![分布式邮件服务接收流程图:SMTP服务器准入策略、垃圾检查与实时推送](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/23. Distributed Email Service/images/email-receiving-flkow.png?utm_source=gitcode_repo_files)

  1. 入站邮件先到SMTP 负载均衡器
  2. SMTP 服务器执行邮件接收策略(无效邮件直接丢弃)
  3. 附件过大时,转存对象存储(S3)
  4. 邮件处理工作器做初步检查(垃圾、病毒)后,把数据写入存储层:元数据库、搜索库、对象存储、缓存
  5. 在线用户通过 WebSocket 实时推送收信;离线用户上线后走 HTTP API 拉取

💾 第3步:元数据数据库设计

邮件元数据的特征

  • 头部小、访问频繁;正文可大可小,但通常只读一次
  • 大多数操作孤立于单个用户(取信、标已读、搜索)
  • 用户通常只读最近的邮件
  • 数据丢失不可接受,可靠性要求最高

在 Gmail 这个量级,数据库通常自研以降低 IOPS。现有方案都有短板:

选项短板
关系型数据库为小数据块优化,不适合大正文列
分布式对象存储适合备份,但搜索/标记已读效率低
NoSQL(如 BigTable)Gmail 所用方案,未开源

关键设计点

  • user_id作为分区键,一个用户的数据落在同一分片
  • 主键 = 分区键(数据分布)+ 聚类键(数据排序)
  • email_id使用TimeUUID,天然按创建时间排序

核心表结构如下(K = 分区键,C↓ = 聚类键降序):

![emails_by_folder邮件表结构:user_id、folder_id分区键与TimeUUID降序排序](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/23. Distributed Email Service/images/emails-table.png?utm_source=gitcode_repo_files)

附件单独建表,以文件名为键,正文中只存对象存储的引用:

![emails_by_user与attachments表结构:附件独立存储并引用URL](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/23. Distributed Email Service/images/attachments.png?utm_source=gitcode_repo_files)

已读/未读过滤在 NoSQL 中是痛点(禁止在非键字段上过滤),解法是把邮件表反范式化为已读/未读两张表:

![read_emails与unread_emails两张反范式化邮件表结构](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/23. Distributed Email Service/images/read-unread-emails.png?utm_source=gitcode_repo_files)

一致性权衡上用可用性换一致性(邮件不能丢是硬性要求),故障切换或网络分区时,同步操作会短暂不可用。

🔍 邮件全文搜索怎么做

与 Google 搜索不同,邮件搜索是用户本地的

Google 搜索邮件搜索
范围整个互联网用户自己的邮箱
排序按相关性按时间、日期等属性
准确性建索引耗时,结果非实时必须快且结果准确

邮件搜索写多读少(每次收/发/删都要重建索引),但用户很少用搜索功能。两种方案:

方案 A:Elasticsearch 集群(推荐)

![Elasticsearch邮件搜索架构:收删邮件经Kafka异步重建索引,搜索同步查询](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/23. Distributed Email Service/images/elasticsearch.png?utm_source=gitcode_repo_files)

  • 变更操作(发/收/删)经Kafka 异步触发重建索引,与服务解耦
  • 实际搜索查询是同步
  • 同样用user_id做分区键,把同一用户的数据分到同一节点

方案 B:自研搜索引擎

  • 用 **LSM 树(Log-Structured Merge-Tree)**组织磁盘索引,写路径只做顺序写优化——Cassandra、BigTable、RocksDB 都采用这一技术:数据先在内存攒批,达到阈值后逐层合并到磁盘

![LSM树分层合并结构:Level 0内存层逐级合并至Level 4磁盘层](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/23. Distributed Email Service/images/lsm-tree.png?utm_source=gitcode_repo_files)

两方案取舍

维度Elasticsearch自研搜索引擎
扩展性够用可为邮件场景精调,扩展上限更高
维护成本独立服务需单独维护可与存储引擎合一
开发成本现成方案需大量工程量

🌍 投递性、扩展性与高可用

**邮件投递性(deliverability)**是个"隐形"难题:新搭的邮件服务器直接发信,大概率进垃圾箱。常用手段:

  • 🆔 使用专用 IP建立信誉
  • 🔥预热 IP(2~6 周)积累良好声誉
  • 🏷️分类邮件——营销邮件不与重要邮件共用发送服务器
  • 🛡️ 用SPF、DKIM等认证技术反钓鱼
  • 🚫 快速封禁垃圾用户,并与 ISP 建立反馈循环跟踪投诉率

扩展性:单个用户的操作互不干扰,多数组件可独立水平扩展

高可用:多数据中心部署 + 主从故障切换:

![多数据中心部署:美国与欧洲数据中心相互复制,主中心故障时用户切到从中心](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/23. Distributed Email Service/images/multi-dc-example.png?utm_source=gitcode_repo_files)

📝 总结与延伸思考

本章分布式邮件服务设计可归纳为 5 点:

  1. 架构:Web / Real-time 双服务器 + 元数据、附件、缓存、搜索四层存储
  2. 异步解耦:收发流程走消息队列,并监控队列做退避与扩容
  3. 存储user_id分区、TimeUUID 排序、已读/未读反范式化、一致性优先
  4. 搜索:Elasticsearch 集群 + Kafka 异步建索引
  5. 可用:多数据中心复制 + 故障切换

面试中还可延伸:容错(节点故障如何处理)、合规(GDPR 下个人数据存储)、安全(邮件加密、反钓鱼)、优化(不同用户重复发送的相同附件去重)。

📚 本章资料:23. Distributed Email Service/README.md;附件对象存储与 24. S3-like Object Storage 的设计思路相通,建议对照阅读。

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

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询