S3 的「/」真的是文件夹吗 - 扁平命名空间的真相与前缀机制

从 S3 不存在文件夹概念、对象键中的「/」只是字符串的一部分这一事实出发,解析前缀搜索的机制、控制台将其显示为文件夹的技巧,以及键设计的最佳实践。

S3 中不存在文件夹

S3 的数据模型是仅由存储桶和对象两层构成的扁平命名空间,不存在类似文件系统的目录层级。对象键 images/2024/photo.jpg 不是「images 文件夹中的 2024 文件夹中的 photo.jpg」,而是「名为 images/2024/photo.jpg 的一个对象」。「/」是键名的一部分,与文件系统的路径分隔符本质上不同。验证这一事实有一个简单方法。使用 AWS CLI 执行 aws s3api list-objects-v2 --bucket my-bucket,所有对象会以扁平列表形式返回,完全不会出现文件夹的概念。而执行 aws s3 ls s3://my-bucket/ 时,则会像存在文件夹结构一样显示。这是因为 s3 命令 (高级命令) 将「/」用作分隔符,把公共前缀显示为文件夹。

控制台将其显示为文件夹的技巧

在 AWS 管理控制台的 S3 界面中,会显示文件夹图标,也有创建文件夹的按钮。然而,在控制台中「创建文件夹」时,实际上创建的是以「/」结尾的零字节对象 (例如 images/)。这个对象只是控制台用来显示为文件夹的标记。即使删除文件夹标记,具有该前缀的对象也不受影响。删除 images/ 对象后,images/photo.jpg 仍然存在。反过来,只要上传 images/photo.jpg,即使没有 images/ 标记,控制台也会显示 images 文件夹。控制台通过在 ListObjectsV2 API 的 Delimiter 参数中指定「/」,将 CommonPrefixes (公共前缀) 显示为文件夹。不理解这一机制的话,会对「删除了文件夹但里面的文件没有消失」「没有创建文件夹却显示了文件夹」等现象感到困惑。

前缀与性能的关系

S3 的前缀直接影响性能。S3 在 2018 年进行了大幅性能改进,每个前缀可处理每秒 5,500 次 GET 请求和 3,500 次 PUT 请求。这里的「前缀」不是对象键中第一个「/」之前的部分,而是键中最后一个「/」之前的部分。例如,images/2024/01/photo.jpg 的前缀是 images/2024/01。也就是说,只要分散前缀,存储桶整体的吞吐量就可以事实上无限扩展。单个前缀每秒 5,500 GET 是上限,但分散到 100 个前缀就可以达到每秒 55 万 GET。2018 年之前,S3 的分区基于键的首字符,因此存在在键开头添加随机哈希 (例如 a1b2c3/images/photo.jpg) 的最佳实践。现在这种对策已不再需要,S3 会自动优化分区。

对象键设计的最佳实践

对象键的设计对 S3 的运维效率有很大影响。第一,统一键的命名规则。基于日期的键 (logs/2024/01/15/access.log) 适合时序数据的管理。S3 的生命周期规则基于前缀应用,因此在前缀中包含日期就可以自动将旧数据迁移到 Glacier 或删除。第二,避免在键中使用特殊字符。S3 的对象键可以使用任意 UTF-8 字符,但包含空格、中文、特殊符号的键可能引发 URL 编码问题。限制使用字母数字、连字符、下划线和斜杠是安全的做法。第三,注意键的长度。对象键的最大长度为 1,024 字节。模拟深层文件夹结构使用长键时会接近这个上限。此外,ListObjectsV2 的响应中包含键名,键越长响应体积越大,消耗更多网络带宽。

S3 Select 与扁平命名空间的活用

S3 的扁平命名空间对习惯传统文件系统概念的开发者来说可能有些不适应,但在大规模数据处理中具有很大优势。扁平命名空间不需要目录元数据管理,因此即使存储数十亿个对象性能也不会下降。在文件系统中,一个目录中有数百万个文件时,仅读取目录元数据就可能需要数秒。S3 Select 是在 S3 侧通过 SQL 查询筛选对象内容、仅返回所需数据的功能。可以对 CSV 或 JSON 格式的对象执行 SELECT 语句,大幅减少数据传输量。例如,从 1GB 的 CSV 文件中仅提取特定列时,使用 S3 Select 只需传输数 MB 的数据。S3 Select 的费用按扫描数据量 (0.002 USD/GB) 和返回数据量 (0.0007 USD/GB) 计算,结合数据传输费用的节省,成本效率很高。要系统学习 S3 的设计模式,可参考相关专业书籍 (Amazon)