第 15 章:设计 Google Drive
引言
Google Drive 是一种云端文件存储与同步服务,让用户能够通过不同设备存储、访问和共享文件。本章讨论如何设计具备以下功能的可扩展系统:
- 文件上传与下载
- 跨设备文件同步
- 文件共享
- 文件版本历史
- 编辑、删除和共享通知
第 1 步:理解问题
核心需求
功能需求:
- 上传和下载文件。
- 在多台设备之间同步文件。
- 保留文件历史版本。
- 支持带权限控制的文件共享。
- 在文件编辑、删除和共享时发送通知。
非功能需求:
- 可靠性: 不能丢失数据。
- 同步速度快: 避免同步延迟让用户久等。
- 带宽效率: 尽量减少不必要的数据传输。
- 可扩展性: 支撑 1000 万日活跃用户(DAU)。
- 高可用性: 在服务器故障或网络问题发生时仍能运行。
约束与假设
- 每位用户获得 10 GB 免费空间。
- 文件大小上限:10 GB。
- 平均上传文件大小:500 KB。
- 每位用户每天上传 2 个文件。
- 所需总存储容量:500 PB。
第 2 步:概要设计
单服务器方案
一个基础方案包含:
Web 服务器: 处理上传和下载。
元数据数据库: 记录用户数据、登录信息和文件信息等元数据。
存储目录: 按命名空间组织文件。

- 设置 Web 服务器,并以
drive/目录作为上传文件的根目录。 drive/下包含一组称为命名空间的目录。- 每个命名空间存放对应用户上传的所有文件。
- 通过命名空间与相对路径的组合,可以唯一标识每个文件或文件夹。
这个方案可以作为起点,但无法满足规模扩展的需要。
API
- 向 Google Drive 上传文件: 支持两种上传方式。
- 简单上传:用于小文件。
- 可恢复上传:
- 接口地址:https://api.example.com/files/upload?uploadType=resumable
- 发送初始请求,取得可恢复上传 URL。
- 上传数据并监控上传状态。
- 如果上传中断,则从断点继续。
- 从 Google Drive 下载文件:
- 获取文件历史版本:
迁移到分布式系统
改进措施:
分片: 根据
user_id将存储分散到多台服务器。Amazon S3: 使用 S3 提供可扩展、具备冗余能力的文件存储,并进行跨区域复制。

负载均衡器: 将流量分配到多台 Web 服务器。
元数据数据库复制: 通过数据库分片和复制保证可用性。
同步冲突:
对于 Google Drive 这样的大型存储系统,同步冲突时有发生。当两位用户同时修改同一个文件或文件夹时,就会产生冲突。

- 例如,用户 1 和用户 2 同时更新同一文件,但系统先处理了用户 1 的文件。
- 用户 1 的更新成功,用户 2 则遇到同步冲突。
- 系统提供同一文件的两个副本:用户 2 的本地副本以及服务器上的最新版本。
- 用户 2 可以合并两个文件,或用其中一个版本覆盖另一个。
改进后的设计

用户交互: 用户通过浏览器或移动应用访问服务。
数据块服务器:
- 将文件切分为最大 4 MB 的数据块,并为每块生成唯一的哈希值。
- 将数据块独立存放在云存储中,例如 Amazon S3。
- 按特定顺序拼接数据块,还原文件。
云存储: 存储数据块,提供可扩展性和冗余能力。
冷存储: 将不活跃文件迁移到冷存储,降低成本。
负载均衡器: 在 API 服务器之间均匀分配请求,保证高效运行。
API 服务器:
- 处理用户认证、个人资料管理及文件元数据更新。
- 管理上传之外的所有工作流。
元数据数据库与缓存:
- 存储用户、文件、数据块及版本的元数据。
- 缓存经常访问的元数据,以加快读取速度。
通知服务:
- 使用发布/订阅系统通知客户端文件变更(添加、编辑、删除)。
- 让客户端能够拉取最新更新。
离线备份队列: 暂存文件变更信息,供离线客户端重新上线后同步。
第 3 步:深入设计
元数据数据库
下面展示的模型经过高度简化,仅包含最重要的表和字段。
表结构设计:
- 用户表: 存储用户资料和偏好设置。
- 文件表: 保存文件元数据,例如大小、名称和路径。
- 数据块表: 跟踪数据块,以便还原文件。
- 文件版本表: 存储文件版本历史。

文件上传流程
- 上传文件:
- 数据块服务器将文件切块、压缩并加密。
- 数据块上传到数据块服务器,并存入 S3。
- 上传元数据:
- 客户端向 API 服务器发送元数据。
- 元数据以
pending状态存入数据库。
- 完成上传:
- S3 触发回调,把文件状态更新为
uploaded。 - 通知服务告知相关用户。
- S3 触发回调,把文件状态更新为

文件同步
增量同步: 只传输修改过的数据块,而非整个文件。

压缩: 根据文件类型选用压缩算法压缩数据块。
冲突解决:
- 先处理的版本生效。
- 冲突版本分别保存,交由用户解决。

文件下载流程
当文件在别处被添加或修改时,就会触发下载流程。客户端可以通过两种方式得知变更:
- 若其他客户端修改文件时客户端 A 在线,通知服务会通知客户端 A。
- 若当时客户端 A 离线,变更数据会保存在缓存中。客户端重新上线后会拉取最新变更。
客户端获知文件变更后,先通过 API 服务器请求元数据,再下载数据块并还原文件。
- 触发: 通知服务告知客户端文件已更新。
- 获取元数据: 客户端通过 API 获取更新后的元数据。
- 下载数据块: 客户端从数据块服务器下载更新的数据块并还原文件。

通知服务
- 目的: 让客户端及时获知文件变更。
- 机制: 采用长轮询实现异步通知。
- 示例: 文件添加、编辑或删除后,将通知推送给所有相关客户端。
存储优化
- 去重: 在账户范围内通过哈希比较移除重复数据块。
- 版本管理策略:
- 限制保存的历史版本数量。
- 对频繁编辑的文件优先保留近期版本。
- 冷存储: 将很少访问的文件迁移到成本更低的存储方案,例如 Amazon S3 Glacier。
故障处理
- 负载均衡器故障: 启用备用负载均衡器。
- 数据块服务器故障: 将待处理任务重新分配给其他服务器。
- 元数据数据库故障:
- 将从节点提升为主节点。
- 将流量重定向到仍可用的副本。
- 云存储故障: 借助跨区域复制读取不可用的文件。
- 通知服务故障: 客户端重新连接其他服务器。