mcpField
B
blob-store

blob-store

github.com/proyecto26云与 DevOps113309 次安装1 天前更新
代码仓库

关于 blob-store

面向云存储设计的技能,说明如何为大型、非结构化对象选择存储层、耐久方案、上传方式和变更模型。适用于图片、备份及数据集等按键读写的对象,并介绍访问控制与成本权衡。

主要功能

  1. 01选择复制或纠删码
  2. 02按访问频率选择存储层
  3. 03使用单次或分段上传
  4. 04规划版本管理与对象生命周期
  5. 05控制访问权限和出口流量

适用场景

  1. 01存储用户上传的图片和视频
  2. 02保存备份与合规归档
  3. 03管理机器学习数据集和大型文件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/proyecto26/system-design-skills --skill blob-store

安装后在 Claude Code 中输入/blob-store确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
deep-dive.md
references/providers/
aws.md
azure.md
gcp.md
generic.md

详细介绍

Blob 存储

在以字符串为键的扁平命名空间中存储大型、不可变、非结构化对象——图像、视频、备份、模型权重、文档 Blob——通过复制确保持久性,并提供直接下载。选错方案,就会把数 MB 的 Blob 塞进按行存储的数据库(导致工作集膨胀、破坏缓存局部性并限制吞吐量),或手工搭建一个会在首次磁盘故障时丢失数据的文件服务器。

适用场景

对象较大(KB 到 GB),只写入一次、读取多次,而且始终按键获取整个对象——绝不会查询对象内部。照片/视频存储、用户上传内容、备份、数据湖/ML 数据集、静态网站资源、日志归档。访问模式是 PUT key → GET key,持久性很重要,而且总数据量太大或访问太冷,不适合放在主数据库中。

不适用场景

需要查询、筛选、排序或联接的小型结构化记录——这类场景应使用 data-storage。需要事务、二级索引或部分更新的数据(Blob 只能整体替换,不能原地编辑)。对小型值进行低延迟读取(KV 缓存或 caching 更合适)。一台机器上的少量文件且永远不会增长——本地文件系统就够用;Blob 存储会带来暂时不需要的运维开销(YAGNI)。把实际属于数据库的工作负载称作“对象存储”,是失效模式 #2。

先确认需求

  • 对象大小分布——平均大小和 p99 大小是多少?(决定分块方式、分段上传阈值,以及读取时是流式传输还是缓冲。)→ back-of-the-envelope。
  • 读写比例与访问时效——是否只写一次、读取多次?数据变冷的速度有多快?(影响分层存储和 CDN 前置。)
  • 持久性与可用性目标——需要几个 9 的持久性?读取能否短暂失败,还是必须始终成功?(副本复制还是纠删码、多区域。)
  • 访问控制——公开、私有,还是按对象授予限时访问权限?(签名 URL。)
  • 可变性与历史记录——对象会变更吗?是否必须保留旧版本(合规、撤销)?(版本控制 + 生命周期管理。)
  • 出口流量情况——谁在读取、从哪里读取、频率如何?(CDN 卸载、出口流量成本。)

可选方案

持久性方案(副本数量及形式)

  • N 路复制——在不同节点/机架/可用区存储 N 个完整副本。适用于对象较小、访问频繁且延迟很重要的场景;最容易理解和管理。
  • 纠删码(EC)——将对象拆分为 k 个数据分片 + m 个校验分片;任意 k 个分片即可重建对象。适用于大规模存储大型/冷数据——持久性与副本复制相同,但开销约为 1.4 倍,而不是 3 倍。(原理见 references/deep-dive.md。)

存储层级(价格/延迟/检索之间的权衡)

  • 热/标准层——毫秒级读取,$/GB 最高。适用于活跃提供服务的对象。
  • 低频/不频繁访问层——存储更便宜,但有检索费用/延迟略高。适用于备份和每月读取几次的数据。
  • 归档/冷层——存储最便宜,检索需要数分钟到数小时。适用于合规保留和极少访问的数据;绝不要用于请求路径上的任何内容。

上传路径

  • 单次 PUT——一个请求。适用于较小的对象(低于分段上传阈值)。
  • 分段上传/可恢复上传——将对象拆分为多个部分,并行上传,按部分重试,完成后提交。适用于大型对象和不稳定网络;超过阈值时默认采用此方案。

变更模型

  • 不可变 + 版本控制——每次写入都会创建一个新版本;删除会创建墓碑。适用于重视历史记录、撤销或防止意外覆盖的场景。
  • 原位覆盖(最后写入者胜出)——最简单;不保留历史记录。适用于只需保留最新对象,且存储旧副本属于浪费的场景。

权衡

选项解决的问题带来的问题何时更改
N 路复制简单、读取快、重建快存储成本至少为 3 倍数据量大且冷,成本占主导 → 纠删码
纠删码以约 1.4 倍的存储量实现相同的持久性每次读取都需要 CPU + 多节点读取;小对象读取慢;重建成本高对象小且热,延迟很重要 → 复制
热存储层低延迟服务每 GB 成本最高数据变冷且很少读取 → 温存储层/归档层
归档层最便宜的静态存储首字节需等待数分钟至数小时;有检索费用任何数据进入对延迟敏感的路径 → 热存储层/温存储层
分段/可续传上传大文件在不稳定网络下也能完成上传;支持并行吞吐客户端逻辑更多;未完成的分段会产生费用对象较小 → 单次 PUT
版本控制撤销、历史记录、防止覆盖存储量会悄然增长;需要设置生命周期过期规则只关心最新版本 → 覆盖,最后写入者胜出
签名 URL将传输负载从应用中卸载;限定访问范围URL 泄露/权限过宽;时钟偏差导致过期错误内容完全公开 → CDN + 公开读取

压力下的行为

Blob 存储通常不会像数据库那样“崩溃”,但会以一些特定方式放大问题。

  • 热点对象/热点前缀: 爆红的文件,或让大量写入共享同一前缀的键方案,会使负载集中到一个分区。缓解措施: 使用 CDN 将热点读取置于前端(content-delivery),对键前缀进行随机化/哈希处理,复制热点对象。
  • 元数据索引瓶颈: 将键映射到分片位置的索引是真正的 SPOF,也是吞吐量上限所在(数百万个小对象造成的影响远大于少数几个大对象)。缓解措施: 对索引分片,缓存热点查询,优先使用数量更少、体积更大的对象。
  • 冷存储层提升时的惊群效应: 对归档数据的一波突发读取会触发缓慢且昂贵的批量检索。缓解措施: 将检索作为异步操作提供,通过队列处理(messaging-streaming),并明确延迟预期。
  • 未完成的分段上传: 已中止的上传会留下分段,悄然累积费用。缓解措施: 设置生命周期规则,在 N 天后中止未完成的上传。
  • 出口流量激增/成本暴涨: 直接从源站提供的热门对象会耗尽带宽并推高出口流量费用。缓解措施: 在前端使用 CDN;源站应为缓存填充提供内容,而不是直接服务终端用户。
  • 写入期间部分失败: 节点在写入中途宕机。缓解措施: 只有达到持久性法定人数(副本或 EC 分片)后才确认写入;后台修复会重新复制持久性不足的对象。

监控: 按操作(PUT/GET/DELETE)统计请求率和错误率、p99 首字节延迟、持久性/修复队列深度、各前缀的热点程度、未完成的分段上传数量,以及出口流量和费用。

如何应用

  1. 明确输入 — 确定对象大小分布、读写比例、持久性目标、访问控制和出站流量情况(参见 先明确这些事项)。如果数据实际上是需要查询的小型结构化记录,请停下——改用 data-storage。
  2. 根据权衡表进行选择 — 选择持久性方案(小型/热点数据采用复制,大型/冷数据采用纠删码)、默认存储层级、按大小确定的上传路径,以及按历史记录是否重要确定的变更模型。
  3. 设置关键参数 — 设计键/前缀方案以避免热点,设置分段上传阈值和分段大小,定义生命周期规则(层级转换、版本过期、中止未完成的上传),并确定签名 URL 的 TTL。
  4. 对选择进行压力测试 — 逐项检查 压力下的行为(热点对象、元数据瓶颈、冷数据检索请求激增、孤立分段、出站流量激增),并确认对于此流量可能触发的问题,都有相应缓解措施。
  5. 用数字估算规模 — 计算含复制/纠删码开销的总存储量、对象数量(元数据索引负载)、峰值 PUT/GET QPS 和月度出站流量。→ 重要数据。
  6. 选择提供商 — 默认采用通用方案;只有用户指定了云服务时才打开提供商文件(参见 选择提供商)。
  7. 在回答中展示权衡 — 提供 Decision | Solves | Worsens | Change it when… 表格,每个此处做出的选择占一行(例如选项、存储方案、故障策略);最后一列应给出具体触发条件(数值或可观察信号)。然后说明最先出问题的环节。

注意事项

应该

  • 将二进制数据保存在存储中,并在数据库中保存指针(键 + 元数据)——绝不要把字节存入行中。
  • 将对象视为不可变;历史记录重要时,应创建新版本,而不是原地编辑。
  • 使用 CDN 承载公开/热点读取,让源站负责填充缓存,而不是直接服务终端用户。
  • 超过大小阈值时使用分段/可恢复上传,并通过生命周期规则中止未完成的上传。
  • 设计键前缀以分散负载;对高基数前缀进行哈希或打乱。
  • 设置生命周期规则,将冷数据转移到更低层级,并自动过期旧版本。

不应该

  • 不要将大型二进制数据存入关系型/NoSQL 行中——这会破坏工作集并降低吞吐量。
  • 不要在读取路径中使用归档层;数分钟到数小时的检索时间会导致请求超时。
  • 不要在没有 CDN 的情况下直接从源站提供热门对象(出站流量会激增)。
  • 不要发放范围宽泛或有效期很长的签名 URL;应严格限制其范围并缩短有效期。
  • 不要以为纠删码没有代价——它会增加 CPU 开销、多节点读取和高昂的重建成本。
  • 不要忽视数百万个微小对象:瓶颈是元数据索引,而不是磁盘。

重要数据

影响设计的指标包括:总存储字节数 × 持久性开销(复制约为 3 倍,纠删码约为 1.3–1.5 倍)、对象数量(元数据索引的规模取决于数量,而不是大小)、峰值上传/下载 QPS,以及月度出站流量(通常是主要成本)。对象存储的持久性目标通常约为 11 个 9;应将其视为由持久性方案设定的设计目标,而不是既定保证。实际的存储/带宽/QPS 计算和单位换算请使用 back-of-the-envelope——此处不要重复其中的表格。

接口示意

此契约精简且通过键寻址:

PUT    /{bucket}/{key}        body=bytes, headers: Content-Type, optional checksum
GET    /{bucket}/{key}        → bytes (supports Range for partial/streamed reads)
DELETE /{bucket}/{key}        → tombstone (new version if versioning on)
HEAD   /{bucket}/{key}        → metadata only (size, etag, version-id)
# multipart: Initiate → UploadPart×N (parallel, retryable) → Complete | Abort
# signed URL: presign(GET|PUT, key, expiry) → time-limited URL the client uses directly

键就是整个索引(例如 userId/2026/photo-uuid.jpg);应同时根据访问模式和前缀分散情况选择键。etag/checksum可让客户端验证完整性并执行条件请求。数据库保存此键及应用元数据,而不是字节。

选择提供商

默认采用上述通用方案。如果用户指定了云服务,请阅读 references/providers/<provider>.md,了解托管服务映射、配额/限制以及提供商特有的权衡。如果该提供商没有对应文件,则采用通用方案。

示意图

若要直观展示上传/下载路径(客户端 → 签名 URL → 存储;CDN 位于 GET 请求前;索引将键映射到分片)或 EC 写入扇出,请使用插件内的 architecture-diagram 技能——将元数据索引显示为独立节点,并将 CDN 显示为边缘层。此处不要 嵌入 Mermaid。

相关构建模块

  • content-delivery — 与此配合:CDN 位于 Blob 源站前,使重复读取 由边缘节点提供服务,存储仅处理缓存填充(边缘缓存由该模块负责)。
  • data-storage — 对于大型非结构化对象,这是替代方案:在此存储 Blob, 在数据库中保存指针(键 + 元数据);元数据的分片/索引由该模块负责。
  • back-of-the-envelope — 存储、对象数量、QPS 和出口流量估算依赖此模块 (用于确定采用复制还是 EC 以及存储层级的数值都在该模块中)。
  • messaging-streaming — 与此配合,用于将冷层检索和上传后处理(转码、缩略图)等异步工作排入队列;交付保证由该模块负责。
  • caching — 与此配合,用于处理热门小对象读取并卸载元数据查询。
  • system-design — 所属概念位于协调器中:推理循环、权衡方法和十种故障模式。

参考资料

  • references/deep-dive.md — 分块、元数据索引、纠删码计算与读取 路径、持久性/修复、版本控制 + 生命周期、分段上传内部机制、签名 URL 机制。 在详细设计存储时阅读。
  • references/providers/{generic,aws,azure,gcp}.md — 各环境中的服务映射、会改变决策的 限制及注意事项。

评论

支持 Markdown