2、对象存储基础:S3协议、桶与对象、存储类与访问控制列表(ACL)

对象存储是现代云原生架构的基石,而 Amazon S3(Simple Storage Service)协议已成为对象存储的事实标准。PostgreSQL 集成对象存储网关(如 pg_parquet、pg_analytics、cstore_fdw 等)均以 S3 兼容 API 作为数据源接入层。理解 S3 协议的核心概念——桶、对象、存储类与 ACL——是优化查询性能与设计存储策略的前提。

2.1 S3 协议概述

S3 协议定义了一套基于 RESTful 的 HTTP/HTTPS 接口,用于对对象存储执行创建、读取、更新、删除(CRUD)操作。其核心设计原则包括:

  • 扁平命名空间:所有数据以“桶(Bucket)+ 对象键(Object Key)”的二元结构寻址,无传统文件系统的目录层级。
  • 最终一致性:部分操作(如覆盖写)在分布式环境下存在短暂不一致窗口,但大多数现代实现已提供读后写一致性。
  • 原子性操作:单个 PUT 操作是原子的,但多部分上传(Multipart Upload)需要显式管理。
  • 元数据扩展:每个对象可附带系统元数据(如 ETag、Content-Length)和用户自定义元数据(以 x-amz-meta- 为前缀)。
PostgreSQL 集成要点:网关插件通过 S3 协议获取对象时,通常使用 Range 请求头实现谓词下推(Predicate Pushdown),只拉取满足过滤条件的字节范围,而非全量对象。这要求对象存储网关支持 HTTP Range 请求。

2.2 桶(Bucket)与对象(Object)

桶是对象的容器,也是权限与存储策略的边界。对象是存储的基本单元,由数据、键和元数据组成。下表对比了桶与对象的关键属性:

维度 桶(Bucket) 对象(Object)
命名空间 全局唯一(所有 AWS 账户共享) 桶内唯一(通过对象键区分)
数量限制 每个账户默认 100 个(可申请提升) 每个桶无数量上限
大小限制 无 单个对象最大 5TB(单次 PUT 最大 5GB)
版本控制 桶级开关 每个对象可保留多个版本
生命周期 桶级策略 按前缀或标签自动转换存储类/过期删除
访问控制 桶策略(Bucket Policy)+ ACL 对象 ACL + 桶策略继承

对象键(Object Key)通常使用 / 分隔符模拟目录结构,例如 logs/2025/04/01/access.log.gz。但需注意:S3 本身并无目录实体,/ 只是键名的一部分。PostgreSQL 网关在扫描对象时,可通过 Prefix 和 Delimiter 参数实现逻辑分区裁剪,减少扫描范围。

性能建议:在 PostgreSQL 外部表定义中,将频繁过滤的日期字段编码到对象键前缀中(如 year=2025/month=04/day=01/),可让网关利用 S3 ListObjects 的 Prefix 参数跳过无关分区,显著提升查询性能。

2.3 存储类(Storage Class)

存储类定义了对象的持久性、可用性、访问延迟与成本。PostgreSQL 网关在查询低频存储类对象时,需注意首次访问延迟(冷启动)。以下是主流存储类的对比:

存储类 持久性 可用性 首字节延迟 最低存储时长 适用场景
S3 Standard 99.999999999% 99.99% 毫秒级 无 热数据、实时分析
S3 Intelligent-Tiering 99.999999999% 99.99% 毫秒级 30天 访问模式不确定
S3 Standard-IA 99.999999999% 99.9% 毫秒级 30天 低频访问但需快速读取
S3 One Zone-IA 99.999999999% 99.5% 毫秒级 30天 可重建的非关键数据
S3 Glacier Instant Retrieval 99.999999999% 99.9% 毫秒级 90天 归档但需即时访问
S3 Glacier Flexible Retrieval 99.999999999% 99.99%(恢复后) 1-5分钟(标准恢复) 90天 长期归档,可接受分钟级延迟
S3 Glacier Deep Archive 99.999999999% 99.99%(恢复后) 12小时(标准恢复) 180天 合规归档,极少访问
查询优化警告:当 PostgreSQL 网关扫描 Glacier 或 Deep Archive 存储类的对象时,若对象处于“冻结”状态(未恢复),S3 将返回 403 InvalidObjectState 错误。网关不会自动触发恢复操作,必须在查询前通过 POST /restore 接口手动恢复对象。建议将分析查询的目标数据至少放在 Standard-IA 或 Glacier Instant Retrieval 中。

2.4 访问控制列表(ACL)

ACL 是 S3 中一种传统的访问控制机制,用于管理桶和对象的读写权限。虽然 AWS 推荐使用更精细的 IAM 策略 和 桶策略,但 ACL 在跨账户共享和兼容旧系统时仍广泛使用。ACL 使用预定义的权限集合:

  • READ:允许列出桶内对象(桶级)或读取对象数据及元数据(对象级)。
  • WRITE:允许创建、覆盖、删除对象(仅桶级有效)。
  • READ_ACP:允许读取 ACL。
  • WRITE_ACP:允许修改 ACL。
  • FULL_CONTROL:上述所有权限。

ACL 的授权对象分为两类:

授权对象 URI 标识 典型用途
账户(Canonical User) 用户 ID(如 a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6) 授予特定 AWS 账户访问权限
预定义组 http://acs.amazonaws.com/groups/global/AllUsers 公开访问(匿名读/写)
http://acs.amazonaws.com/groups/global/AuthenticatedUsers 所有经过身份验证的 AWS 用户
http://acs.amazonaws.com/groups/s3/LogDelivery 仅用于 S3 服务器访问日志写入
PostgreSQL 网关安全实践:当使用 PostgreSQL 外部表访问 S3 时,建议在桶策略中限制网关使用的 IAM 角色仅具有 s3:GetObject 和 s3:ListBucket 权限,并禁用桶的公开 ACL。避免在对象级 ACL 中授予 AllUsers 写权限,防止数据被意外覆盖或删除。

2.5 S3 协议核心交互流程

下图展示了 PostgreSQL 网关通过 S3 协议与对象存储交互的典型流程,重点体现查询优化中的关键步骤:

PostgreSQL 网关 S3 协议交互流程 PostgreSQL 网关 S3 协议交互流程 PostgreSQL 查询 网关:谓词下推 S3 ListObjects S3 GetObject (Range) 对象存储集群 返回列式数据块 返回查询结果 关键优化点说明: 1. 网关根据 WHERE 条件提取分区键(如 year、month),在 ListObjects 时设置 Prefix 参数,跳过无关对象。 2. 对于列式格式(Parquet/ORC),网关通过 Range 请求只拉取包含目标列的字节范围,减少网络传输。 3. 对象存储集群返回的数据直接以列式块形式流入 PostgreSQL 执行引擎,避免中间格式转换。

2.6 桶与对象的逻辑结构

下图展示了桶内对象的逻辑组织方式,以及 PostgreSQL 网关如何利用键前缀实现分区裁剪:

桶与对象的逻辑结构及分区裁剪 桶与对象的逻辑结构及分区裁剪 桶:analytics-data (us-east-1) year=2025/ month=04/ day=01/ data_001.parquet data_002.parquet data_003.parquet 分区裁剪 WHERE year=2025 AND month=04 AND day=01 → 仅扫描 3 个对象 跳过 997 个无关对象

2.7 存储类选择策略

在 PostgreSQL 集成场景中,存储类的选择直接影响查询延迟与成本。建议遵循以下策略:

  1. 热数据(最近 30 天):使用 S3 Standard 或 Intelligent-Tiering,确保毫秒级首字节延迟,支持高频分析查询。
  2. 温数据(30-90 天):使用 S3 Standard-IA,降低存储成本,仍保持毫秒级访问。
  3. 冷数据(90 天以上):使用 S3 Glacier Instant Retrieval,存储成本大幅降低,但需注意最低 90 天存储时长。
  4. 归档数据(合规保留):使用 S3 Glacier Deep Archive,仅在审计或回溯查询时手动恢复。
自动化策略:在桶上配置生命周期规则,自动将对象按时间迁移至更低成本的存储类。例如:transition: 30d -> Standard-IA; 90d -> Glacier Instant Retrieval; 365d -> Deep Archive。PostgreSQL 网关在查询时需感知存储类,避免直接扫描冻结对象。

2.8 ACL 配置示例

以下示例展示如何通过 AWS CLI 设置桶 ACL,允许特定账户读取对象:

# 授予账户 ID "123456789012" 对桶 "my-analytics-bucket" 的 READ 权限
aws s3api put-bucket-acl \
  --bucket my-analytics-bucket \
  --grant-read 'id=123456789012'

# 授予所有经过身份验证的用户对对象 "logs/2025/04/01/data.parquet" 的读取权限
aws s3api put-object-acl \
  --bucket my-analytics-bucket \
  --key logs/2025/04/01/data.parquet \
  --grant-read 'uri=http://acs.amazonaws.com/groups/global/AuthenticatedUsers'

# 查看当前 ACL
aws s3api get-bucket-acl --bucket my-analytics-bucket
安全最佳实践:在生产环境中,避免使用 AllUsers 组授予公开读写权限。对于 PostgreSQL 网关访问,应使用 IAM 角色与桶策略组合,而非 ACL。桶策略支持条件键(如 aws:SourceIp、aws:Referer),提供更细粒度的控制。

2.9 本章小结