LabHub
学习 学习路径 课程

对象存储与 S3

分片上传与 ETag 的算术

在 LabHub 中继续学习

一句话总结

分段上传的 ETag 不是内容的 md5。它是将各分段 md5 的二进制值依次拼接后再次计算 md5,并在末尾附加-파트수得到的值。

概念图: 附加校验和 · S3 每收到一个分段都会验证 · 创建存储桶时就应加入的默认配置

为什么需要了解这些

用一次 PUT 上传 5GB 文件时,如果连接在 90% 处中断,就必须从头开始。而且单条 TCP 连接无法用尽全部带宽。

分段上传同时解决了这两个问题。先启动上传并取得 UploadId,再把文件拆成多个分段并行上传,最后提交分段列表来宣告完成。如果中途断开,只需重新上传失败的那个分段。

工作原理

规则中有几个明确数字。分段最多 10,000 个;除最后一个分段外,每段至少为 5MiB;单个分段最大为 5GiB。三者相乘便得到单个对象的最大大小——5GiB × 10,000,约为 5TB。

分段大小的选择是一项权衡。分段越小,请求数越多(对于按请求计费的服务,这意味着成本增加);分段越大,重传代价越高。实际工作中常用 8~64MiB。分段大小还必须大于文件大小除以 10,000 的结果。

ETag 的计算方式很有意思。单分段上传时,它就是内容的 md5;分段上传时,则按如下方式计算。

1. 각 파트의 md5 를 이진(16바이트)으로 구한다
2. 그것들을 순서대로 이어 붙인다 (파트 5개면 80바이트)
3. 그 80바이트의 md5 를 구한다
4. 뒤에 "-5" 를 붙인다

因此,ETag 中包含连字符就表示该对象通过分段方式上传,它绝不会等于本地文件的 md5。大量“用 ETag 校验完整性却不一致”的疑问都源于此。若要验证,必须按相同分段大小拆分,并执行相同计算。

分段大小的计算方法

分段大小不能凭感觉决定。两个约束会确定它的上限和下限。

하한 = 파일 크기 ÷ 10,000      (파트 수 제한)
하한 = 최소 5 MiB              (마지막 파트 제외)
상한 = 5 GiB

100GB 파일이면 → 100GB ÷ 10,000 = 10MB 이상이어야 한다
                → 16MiB 로 잡으면 6,400 파트. 여유가 있다

接下来要考虑重传成本。如果网络不稳定,平均有 2% 的分段失败,那么分段越大,每次失败所浪费的字节数就越多。反过来,分段越小,请求数越多,开销和请求费用也越高。

文件大小 推荐分段 分段数 说明
~100MB 不需要分段上传 1 单次 PUT 更简单
100MB~1GB 8~16 MiB 12~64 并发数 4~8 即可
1GB~50GB 32~64 MiB 30~800 并发数 8~16
50GB 以上 64~128 MiB 400~800 先确认分段数上限

并发数由带宽和内存决定。分段大小乘以并发数的容量会同时进入内存,因此 64MiB × 16 = 1GB。如果超过容器内存限制,进程就会因 OOM 而终止。

使用校验和验证完整性的正确方法

试图用 ETag 验证完整性,在分段上传时会失效。应改用 S3 提供的附加校验和。上传时指定算法,S3 就会逐段验证,并保存整个对象的校验和。

s3.put_object(Bucket=b, Key=k, Body=data, ChecksumAlgorithm="SHA256")

r = s3.head_object(Bucket=b, Key=k, ChecksumMode="ENABLED")
print(r.get("ChecksumSHA256"))

在分段上传中,这个值同样采用组合算法(拼接各分段校验和后再次哈希),但由于S3 每收到一个分段都会验证,传输中的损坏会当场被发现。这比手工重新计算 ETag 安全得多。

CRC32C 计算速度快,适合大容量数据;SHA256 较慢,但具备更强的密码学特性。如果目的只是检测传输错误,CRC32C 就足够了。

查找并清理中断的上传

# 이 버킷에 떠도는 미완료 업로드
aws s3api list-multipart-uploads --bucket my-bucket

# 하나 지우기 — 올라간 파트가 모두 사라진다
aws s3api abort-multipart-upload   --bucket my-bucket --key big.tar --upload-id <UploadId>

手动查找迟早会被遗忘。正确做法是通过生命周期规则实现自动化(AbortIncompleteMultipartUpload,7 天)。这条规则是创建存储桶时就应加入的默认配置

生产现场会看到什么

中断的分段上传会悄无声息地消耗资金。未完成上传的分段占用存储空间,却不会作为对象显示在列表中。如果客户端崩溃或部署过程中中断的上传累积数月,账单上就会莫名出现数百 GB。这是 AWS 中常见的成本泄漏项。

应对方式有两项:应用程序在失败时显式执行中止(abort),并在存储桶生命周期规则中把 AbortIncompleteMultipartUpload 设为约 7 天。将后者作为默认规则更安全。

下一次实验要做什么

把一个 24MiB 文件拆成 5MiB 分段,亲手执行分段上传。取得 UploadId,上传各分段并完成上传,然后手工计算分段 ETag,与服务器返回值核对。最后还要尝试只启动上传而不完成,使其处于中断状态。