在印度机房用阿里云 OSS 同步备份,multipart 分片把 200GB 传成了 340GB

深夜两点,备份窗口只剩四小时。印度孟买机房一台存储节点,Debian 12,内核 6.1,要往阿里云 OSS 推 200GB 的数据库全量备份,工具用 ossutil 1.7.16。现象很怪:本地 du -sh 是 200GB,OSS 控制台显示占用 340GB,而且上传速度在后半段掉到 3MB/s,日志里反复出现 PartUploadFailed, retrying part 37。

先看 ossutil 的配置和命令,这是我当时用的:

ossutil64 cp -r /backup/mysql/ oss://db-backup/mysql/ \
  --bigfile-threshold 104857600 \
  --part-size 52428800 \
  --jobs 4 \
  --checkpoint-dir /var/tmp/ossutil-ckpt \
  -f

问题出在两个地方。第一,--part-size 52428800(50MB)配得太小,200GB 会被切成 4000 多个分片,每个分片单独发请求,孟买到 OSS 新加坡区域的 RTT 约 65ms,连接复用没做好时,每个分片至少三次往返,时间全耗在握手和重试上。第二,--checkpoint-dir 放在 /var/tmp,而机器上有清理脚本每天凌晨三点清 /var/tmp 下超过 24 小时的文件。断点记录被删掉后,ossutil 重启就把已经上传的分片当不存在,重新传一遍,于是 OSS 上出现了大量未合并的碎片分片,存储量虚高。

做法是先把分片调大,再给断点目录换一个不会被清的位置:

mkdir -p /backup/.ossutil-ckpt
ossutil64 cp -r /backup/mysql/ oss://db-backup/mysql/ \
  --bigfile-threshold 5368709120 \
  --part-size 268435456 \
  --jobs 8 \
  --checkpoint-dir /backup/.ossutil-ckpt \
  --enable-symlink-dir \
  -f

--part-size 提到 256MB,分片数量从 4000 多降到 800 左右;--jobs 8 让并发连接更充分地利用带宽。重传前先清掉之前遗留的碎片:

ossutil64 ls oss://db-backup/mysql/ --all-versions | grep -E "multipart|part" | awk '{print $NF}' | xargs -I{} ossutil64 rm oss://db-backup/mysql/{}

机制上说,OSS 的 multipart 上传在分片全部完成前不会合并,未完成的分片会计入存储,但不出现在普通 ls 里,只有带 --all-versions 或看碎片管理才能看到。代价是分片越大,单个分片失败重传的粒度越粗,256MB 在 65ms RTT 下如果中途断一次,重传要 80 秒左右,所以 --jobs 不能开太大,否则失败重试会互相挤占带宽。

边界条件也很明确:--part-size 不能超过 5GB,OSS 单对象上限是 48.8TB,但分片数最多 10000,200GB 用 256MB 分片是安全的,如果备份到 2TB 以上就要重新算。另外,--checkpoint-dir 必须和备份数据在同一个持久卷上,别放内存盘或会被 TTL 清理的目录。我自己踩的坑是第一次重传时忘了加 -f,ossutil 检测到目标对象已存在直接跳过,结果一半文件是旧版本,校验和全对不上。

你们在跨境链路上传大备份时,分片大小是按 RTT 算的还是按对象总大小算的?断点目录一般放在哪?

0
赞同度 100% · 0 条评论 · 5 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。