当备份目标为 S3 时,如何使用“multipart”上传 50 GB 单库?
解读
面试官真正想确认的是:
- 你是否理解 CouchDB 备份本质是顺序导出一份 .couch 文件,而非“SELECT *”式逻辑备份;
- 你是否知道 50 GB 单文件直接 PUT 会触发 S3 单对象 5 GB 限制,必须改用 multipart;
- 你能否把 CouchDB 的离线快照、并发分块、失败重传、成本优化、权限模型在中国区 S3(北京、宁夏)合规落地;
- 你能否给出可脚本化、可回滚、可验证的完整方案,而不是只背“aws s3 cp”参数。
知识点
- CouchDB 文件级一致性:备份前必须暂停写入或利用
/_backup端点(2.3+)做快照,否则 .couch 可能处于不一致状态。 - S3 Multipart Upload 协议:每个分片 5 MB–5 GB,最多 10 000 片;Initiate → Upload Parts → Complete,支持断点续传。
- 中国区 AK/SK 合规:必须使用国内官方 SDK(boto3、aws-cli 2.x)+ 北京/宁夏区域域名,避免海外 endpoint 被墙。
- 成本与限速:跨区域流出流量费约 0.49 元/GB;若同区上传走内网,流量 0 元但需走 VPC Endpoint 避免 NAT 费用。
- 完整性校验:每片在上传前计算 CRC32-C 或 SHA-256,Complete 时带上
ChecksumAlgorithm参数,S3 会二次校验。 - 回滚策略:若 Complete 失败,24 h 内未调用 Abort,会按已上传分片占用空间计费;脚本必须捕获异常主动 Abort。
- CouchDB 版本差异:1.x 需停写;2.x 可用
/_backup触发快照;3.x 支持热备份,但快照仍推荐在低峰期执行。
答案
-
快照阶段
a. 低峰期执行curl -X POST http://localhost:5984/_backup -d '{"type":"file","name":"shop_50gb"}'
b. 等待返回"state":"completed",得到/var/lib/couchdb/backup/shop_50gb.couch文件,确认大小 50 GB。 -
分片规划
采用 100 片 × 512 MB,单片 512 MB 既小于 5 GB 上限,又保证断点重传粒度可控;并发 8 线程,在中国区北京 region 内网走 VPC Endpoint,节省流量费。 -
上传脚本(Python 3 + boto3)
import boto3, os, hashlib, threading
from concurrent.futures import ThreadPoolExecutor
file_path = '/var/lib/couchdb/backup/shop_50gb.couch'
bucket = 'couchdb-backup-cn'
key = 'couchdb/shop_50gb.couch'
part_size = 512 * 1024 * 1024 # 512 MB
s3 = boto3.client('s3', region_name='cn-north-1',
endpoint_url='https://s3.cn-north-1.amazonaws.com.cn')
mpu = s3.create_multipart_upload(Bucket=bucket, Key=key,
ChecksumAlgorithm='CRC32C')
upload_id = mpu['UploadId']
parts = []
lock = threading.Lock()
def upload_part(idx, start, end):
with open(file_path, 'rb') as f:
f.seek(start)
data = f.read(end - start)
crc = hashlib.crc32c(data).digest()
resp = s3.upload_part(Bucket=bucket, Key=key, PartNumber=idx,
UploadId=upload_id, Body=data,
ChecksumCRC32C=crc.hex())
with lock:
parts.append({'ETag': resp['ETag'], 'PartNumber': idx,
'ChecksumCRC32C': resp['ChecksumCRC32C']})
size = os.path.getsize(file_path)
futures = []
with ThreadPoolExecutor(max_workers=8) as exe:
for i in range(1, (size // part_size) + 2):
start = (i-1) * part_size
end = min(i * part_size, size)
if start >= size:
break
futures.append(exe.submit(upload_part, i, start, end))
# 异常捕获省略,生产环境需 try/except 并调用 abort_multipart_upload
parts.sort(key=lambda x: x['PartNumber'])
s3.complete_multipart_upload(Bucket=bucket, Key=key,
UploadId=upload_id,
MultipartUpload={'Parts': parts})
print('backup done')
- 验证与清理
使用aws s3api head-object --bucket couchdb-backup-cn --key couchdb/shop_50gb.couch确认"Size": 53687091200;
本地计算sha256sum shop_50gb.couch并与下载后文件比对,哈希一致即成功;
24 h 内若脚本异常退出,定时巡检任务会扫描未完成 UploadId 并调用abort_multipart_upload防止空计费。
拓展思考
- 增量策略:CouchDB 2.3+ 支持
/_backup?incremental=true,但底层仍是全量快照;若数据量大,可改用 连续复制到另一集群 做逻辑增量,再对新集群做快照,降低 RPO。 - 加密与合规:中国区金融客户需KMS 国密算法;可在
create_multipart_upload时指定ServerSideEncryption='aws:kms'并传入SSEKMSKeyId,保证数据落盘加密。 - 成本再优化:512 MB 分片在 10 Gbps 内网链路下 8 并发即可跑满带宽;若备份窗口充裕,可调大到 1 GB 分片、4 并发,减少 API 调用次数(每万次 0.45 元),节省约 15 % 请求费用。
- 多云容灾:同一脚本只需替换 endpoint 即可将分片并行上传到阿里云 OSS 或腾讯云 COS 的类似 Multipart 接口,实现双云对拷;注意国内云厂商的 PartSize 上限差异(OSS 最大 5 GB 同 S3,COS 最大 100 MB 需调整)。