· 王思腾 · 系统架构 · 9 分钟阅读

系统设计:如何设计一个云盘(网盘)系统

云盘的核心不是“把文件存到服务器上”,而是支撑海量小文件与超大文件的上传下载、断点续传、秒传去重、分块存储与弹性扩容。本文从文件元数据与内容分离讲起,覆盖分块上传、秒传(内容哈希去重)、对象存储、目录树模型与分享权限,带你设计一个类百度网盘/Google Drive 的系统。

云盘的核心不是“把文件存到服务器上”,而是支撑海量小文件与超大文件的上传下载、断点续传、秒传去重、分块存储与弹性扩容。本文从文件元数据与内容分离讲起,覆盖分块上传、秒传(内容哈希去重)、对象存储、目录树模型与分享权限,带你设计一个类百度网盘/Google Drive 的系统。

云盘(网盘)看起来就是个“远程文件夹”,但要支撑海量文件(几 KB 文档到几 GB 视频)、断点续传、秒传、多设备同步、分享,背后是典型的“元数据 + 内容存储”分离的分布式存储系统。

一、核心需求

  • 文件/文件夹的上传、下载、删除、移动、重命名;
  • 目录结构(树形),像本地文件管理器一样浏览;
  • 大文件分块上传 + 断点续传(传到一半断了不用重来);
  • 秒传:别人传过的相同文件,自己瞬间“上传”完成;
  • 多端同步、文件分享(链接 + 权限/过期);
  • 海量容量弹性扩展,数据多副本可靠。

二、关键思想:元数据与内容分离

这是云盘设计的第一原则:

  • 元数据(Metadata):文件名、大小、所属目录、上传者、时间、权限,以及“这个文件由哪些内容块组成”——存在关系型数据库(MySQL 分库分表),要支持事务和目录查询;
  • 内容(Content):文件本身的二进制字节,存在对象存储(S3/OSS/自建分布式文件系统),按内容哈希寻址。

两者通过内容块的哈希关联。为什么要分离?因为内容是巨大、不可变、只追加的字节流,适合对象存储;元数据是小而频繁变更、需要结构化查询和事务的,适合数据库。

flowchart TB
    Client["客户端"] --> API["API 服务<br/>鉴权 · 元数据操作 · 上传调度"]
    API --> Meta[("元数据库 MySQL<br/>文件/目录/用户/权限")]
    API --> Up["上传/下载服务"]
    Up --> OSS[("对象存储集群<br/>按内容哈希分块存储 · 多副本")]
    API --> Cache[("Redis<br/>秒传哈希索引 · 会话")]

三、分块上传与断点续传

大文件不能一个 HTTP 请求传完(网络会断、超时、无法重试)。做法是客户端把文件切成固定大小的块(如 4MB/8MB),逐块上传:

sequenceDiagram
    participant C as 客户端
    participant S as 服务端
    participant O as 对象存储
    C->>C: 文件切分成 block 1..N,逐块算 hash
    C->>S: 初始化上传(文件信息) → 返回 uploadId
    loop 每个块
        C->>S: 上传块(uploadId, 块序号, 块hash, 数据)
        S->>O: 存储该块
        S-->>C: 返回该块 ACK
    end
    Note over C: 中断后重连:凭 uploadId 查已传块,只续传缺失块
    C->>S: 全部完成 → 提交合并
    S->>S: 校验块列表、组装元数据、建立文件记录
    S-->>C: 上传完成
  • 断点续传:上传中断后,客户端拿 uploadId 问服务端“哪些块已经传好了”,只补传缺失的块;
  • 每块独立上传、可并发、可重试,失败只需重传那一块;
  • 上传完成后服务端校验块哈希,确认完整再合并。

四、秒传:内容哈希去重

“秒传”的本质是内容寻址去重:

  1. 客户端在上传前,先对整个文件算一个强哈希(如 SHA-256,配合大小、分块哈希防碰撞);
  2. 把哈希发给服务端查询:这个内容库里已经有了吗?
  3. 已有 → 直接在用户目录下新建一条元数据记录,指向已有的内容块,不传一个字节,瞬间完成(秒传);
  4. 没有 → 走正常分块上传。
flowchart TB
    F["选择文件"] --> H["客户端计算文件哈希"]
    H --> Q{"服务端哈希索引<br/>已存在该内容?"}
    Q -- 存在 --> Fast["秒传:新建元数据<br/>直接引用已有内容块(0 上传)"]
    Q -- 不存在 --> Up["分块上传各数据块"]
    Up --> Store["对象存储保存块 + 登记哈希索引"]

这同时实现了跨用户去重:一万个人传同一个安装包,对象存储里只存一份内容(引用计数),极大节省空间。删除文件时只是删元数据、把引用计数减一,归零才真正回收内容块。

五、目录树与元数据模型

文件系统是树状结构,常见两种建模:

  • 邻接表(parent_id):每个文件/目录记录 id、name、parent_id、is_dir、content_ref。查某目录下的内容就是 WHERE parent_id = ?,移动/重命名只改一行,简单高效——云盘主流方案;
  • 路径枚举/嵌套集:适合深层级路径查询,但更新成本高,较少用。
flowchart TB
    Root["根目录 / (id=1)"] --> A["工作资料/ (id=2)"]
    Root --> B["照片/ (id=3)"]
    A --> F1["需求.docx<br/>content=hash:abc..."]
    A --> F2["设计稿.psd<br/>content=hash:def..."]
    B --> F3["旅游.jpg<br/>content=hash:xyz..."]

移动、重命名只改元数据记录,不动对象存储里的内容(内容是不可变的字节,与路径无关)。

六、分享与权限

分享本质是“给资源发一张带权限的访问凭证”:

  • 生成分享链接 + 提取码,背后是一条分享记录(资源 id、有效期、访问者权限[预览/下载/编辑]、是否需登录);
  • 访问分享链接时,服务端校验凭证有效性与权限,再签发对对应内容块的临时下载 URL(对象存储的预签名 URL,直连下载、不经业务服务器转发,省带宽);
  • 私有文件则通过登录态 + 所有者校验保护。

七、可靠性与扩展性

  • 对象存储多副本/纠删码:内容块存 3 副本或用纠删码,坏盘不丢数据,节点可水平扩容;
  • 元数据分库分表:按用户 ID 分片,支撑海量用户与文件记录;
  • 上传下载走客户端直连对象存储(预签名 URL),业务服务器只做调度和元数据,不中转大流量,避免成为带宽瓶颈;
  • CDN 加速下载:热门文件边缘缓存;
  • 去重和冷热分层(老文件转低频存储)进一步压成本。

八、小结

  1. 第一原则是元数据与内容分离:结构化的文件/目录/权限信息放数据库,二进制内容放对象存储,用内容哈希关联;
  2. 大文件靠分块上传 + 断点续传:切块、并发、可重试,中断后只补缺失块;
  3. 秒传 = 内容哈希去重:上传前查哈希,命中则只建元数据、零上传,同时实现跨用户去重省空间;
  4. 目录用 parent_id 邻接表建模,移动/重命名只改元数据、不碰内容;
  5. 分享靠凭证 + 预签名 URL 直连对象存储,可靠性靠多副本/纠删码 + 元数据分片 + CDN。

云盘的本质是一个“内容寻址、元数据驱动”的分布式存储系统——理解了“元数据归元数据、字节归字节”,整套设计就顺了。

本文由 AI 辅助创作,所有内容仅供交流与参考。
分享:
返回博客
系统设计:如何设计搜索自动补全(搜索建议)系统

系统设计:如何设计搜索自动补全(搜索建议)系统

在搜索框敲下几个字,下拉框立刻给出最热门的补全建议——这个功能要求在几十毫秒内、从海量词条里返回“以当前输入为前缀、且最热门”的 Top-K。本文讲清前缀匹配的数据结构(Trie/字典树)、热度排序 Top-K、查询日志离线构建词典、在线低延迟服务与缓存,带你设计一个类 Google/淘宝的搜索建议系统。

系统设计:如何设计一个聊天系统(即时通讯 IM)

系统设计:如何设计一个聊天系统(即时通讯 IM)

聊天系统看似简单(发消息、收消息),背后却要解决实时推送、消息有序、已读回执、离线消息、多端同步、海量连接等一系列难题。本文从收发消息的核心链路入手,讲清长连接网关、消息投递与确认、存储模型、群聊扇出,以及可靠性(不丢、不重、有序)的设计取舍。

个人与小团队的全栈技术栈:前端、后端到持续部署,怎么做到低成本、快交付

个人与小团队的全栈技术栈:前端、后端到持续部署,怎么做到低成本、快交付

一个人或两三个人做产品,最大的约束不是技术,是时间和钱。这套栈的目标很明确:不养服务器、不写运维、Git push 就上线、流量小的时候几乎零成本。本文从前端静态优先、后端 Serverless、托管数据库,到 CI/CD 自动部署,给出一套能今天动手、明天上线、跑起来月账单趋近于零的务实组合,顺便拿这个网站本身当例子。