这是 Beta 探索课程,内容结构、实验步骤和示例可能会继续调整。
对象 key 设计与元数据一致性
对象 key 不要用原始文件名
对象存储的 key(路径)是文件的「物理地址」。不要直接使用原始文件名,推荐包含业务、日期和随机 ID:
uploads/{tenant_id}/{yyyy}/{mm}/{dd}/{file_id}.{ext}
这样做的好处:
- 避免同名文件覆盖。
- 支持按租户和日期做清理、统计、迁移。
- 不暴露用户上传的原始文件名。
- 方便接入冷热分层和生命周期策略。
原始文件名只保存在元数据表里,用于展示和下载时的 Content-Disposition——用户看到的是「合同.pdf」,存储里是「uploads/…/x9f3.pdf」。
对象存储不知道业务
对象存储只知道「有一个对象」,不知道它属于哪个用户、是否通过审核、能不能下载。因此数据库仍然是业务事实来源:
created -> uploading -> uploaded -> scanning -> available
-> rejected
-> deleted
一致性问题:谁先谁后
迁移到对象存储后,存在两类一致性问题:
- 对象上传成功,但数据库更新失败 → 对象存在、记录缺失(孤儿对象)。
- 数据库记录存在,但对象不存在 → 记录存在、内容缺失(悬挂记录)。
不要强行做分布式事务,而是用状态机和补偿任务修复:
uploading状态超时未完成 → 清理临时对象。uploaded状态迟迟未进入扫描 → 重新投递扫描任务。- 定期比对数据库与对象存储 → 发现孤儿文件 / 悬挂记录并清理。
幂等与重试
对象存储上传可能重试,业务侧也要幂等:
- 重复的完成通知,不产生重复记录。
- 回调重试要有幂等键。
- 状态流转只允许「前进」,防止回退造成混乱。
小结
- 对象 key 用「业务 / 日期 / 随机 ID」,避免同名覆盖、便于治理。
- 数据库是业务事实来源,对象存储只管内容。
- 一致性问题靠状态机 + 补偿任务修复,不靠分布式事务。
下一节看对象存储最容易被忽视的一面:生命周期与成本。
发现内容有问题?
告诉我们哪里需要改进。
