# AWS 大数据深度剖析（第四部分）：Glue Catalog、Athena 与 Lake Formation

> AWS Glue Data Catalog 如何充当数据湖的中央目录，以及 Athena 如何用无服务器 SQL 查询 S3 上的 Parquet 与 Iceberg 表。

- 作者: zhuermu
- 发布: 2025-05-13
- 网页版: https://zhuermu.com/blog/bigdata-deep-dive-part-4-metadata-query-engines/

---
> 数据已经落到了 S3 上——Athena、Spark、SageMaker 怎么知道「这是一张表，它有这些列」？答案是：**Glue Data Catalog**。
>
> 表注册好之后，谁来执行 SQL？答案是：**Athena**（无服务器）/ **EMR Spark**（重负载）/ **Redshift**（高并发 BI）。
>
> 谁来控制「哪些字段能被哪些人看到」？答案是：**Lake Formation**。

---

## 为什么你需要一个中央元数据目录

没有 Catalog 会发生什么？下面是一个常见的灾难场景：

```
团队 A：在 Athena 中创建了一张按 dt 分区的 `events` 表
团队 B：用 EMR Spark 以自己的 schema 读取 events，发现某个列的类型对不上
团队 C：用 SageMaker 做训练，CSV 模式 + 硬编码列名
```

三个团队，三套 schema，没人知道哪一套才是对的——数据治理就此崩溃。

**Glue Data Catalog 就是唯一可信来源（single source of truth）**：每个引擎都从同一个地方读取表定义——一个元数据存储统御一切。

![Glue Catalog 架构](/images/blog/bigdata-deep-dive/04-glue-catalog.svg)

---

## AWS Glue Data Catalog 详解

### 它是什么——以及它不是什么

**它是**：一个**只存储元数据**的服务（即 Metastore）。
**它不是**：数据库（它不存业务数据），也不是查询引擎（它不跑 SQL）。

打个比方：它就像图书馆的卡片目录——它告诉你「《活着》这本书在 3 楼 B 区第 5 排第 12 个位置」。书本身在 3 楼。

### 三级层次结构

```
Glue Data Catalog                ← 每个 AWS 账户一个（全局）
└── Database                     ← 命名空间（类似于一个「schema」）
    └── Table                    ← 单张表
        ├── Schema（列名、类型）
        ├── Location (s3://...)  ← 数据在 S3 中的位置
        ├── Partitions (dt=...)  ← 分区定义
        ├── Storage format（Parquet / Iceberg / CSV）
        └── 其他属性
```

### 数据进入 Catalog 的四种方式

| 方式 | 最适用场景 | 自动化程度 |
|---|---|---|
| **Zero-ETL / 基于 DMS 的 Zero-ETL** | Aurora / RDS MySQL CDC | 全自动 |
| **Glue Crawler** | 已有的 S3 数据；自动推断 schema 并注册 | 高 |
| **Glue ETL Job（写入时注册）** | Spark 写入新分区并自动注册 | 中 |
| **手动 CREATE TABLE / Terraform** | 完全受控的生产环境 | 低——但最稳定 |

**生产最佳实践**：用手动 / Terraform 来定义 ODS / DWD / DWS / ADS 的表 schema（可控）。Crawler 只用于探索性数据。

### 什么是 Glue Crawler？

Crawler 是一个「扫描 S3 并自动建表」的工具：
- 你给它一个 S3 路径
- 它扫描几个文件，推断列类型和分区
- 它在 Catalog 中自动注册这张表

**优点**：方便。
**缺点**：推断出的类型可能出错（例如把 string 推断成 int），分区检测也可能不稳定。在生产环境中，务必审查 Crawler 的输出。

### Iceberg 表是特殊的

Iceberg 表不会把 Parquet 文件路径注册到 Catalog 中，而是注册一个**指向 Iceberg metadata.json 文件的指针**：

```
ods_user 表在 Catalog 中的条目：
  TBLPROPERTIES:
    table_type = 'ICEBERG'
    metadata_location = 's3://.../ods_user/metadata/v123.json'
```

每次写入时：Iceberg 会写出一个新的 metadata.json，然后 Catalog 原子性地将 `metadata_location` 指针更新到新版本。

**关键洞察**：每个读取 Iceberg 表的引擎，都会先从 Catalog 获取当前的元数据指针，然后解析元数据以得到一份精确的文件清单（manifest）。这正是数据湖中实现 ACID 事务的方式。

### 跨账户与跨区域

- 同账户、同 Region：直接共享
- 跨账户：通过 **Lake Formation 资源共享（Resource Sharing）** 授权访问
- 跨 Region：使用 **Glue 跨区域 Catalog（Cross-Region Catalog）**（2024 年起可用）

### 定价

非常便宜：
- 前 100 万个对象（表 + 分区）：免费
- 之后：每月每 100 万个对象 1 美元
- API 请求：每月前 100 万次免费，之后每 100 万次 1 美元

在实践中，对于任何真实的数据仓库，这项成本都可以忽略不计。

**官方文档**：
- [Glue Data Catalog](https://docs.aws.amazon.com/glue/latest/dg/components-overview.html)
- [Glue Crawler](https://docs.aws.amazon.com/glue/latest/dg/add-crawler.html)

---

## Amazon Athena：无服务器 SQL 查询

### 什么是 Athena？

**一句话概括**：给它一张已在 Glue Catalog 中注册的表，再加上一条 SQL 查询，它就会执行查询并返回结果。**无服务器，按扫描量计费。**

其底层引擎是 **Trino**（前身为 Presto，最初由 Facebook 开源的分布式 SQL 引擎），由 AWS 托管并优化。

### 一次查询在内部是如何执行的

![Athena 执行流程](/images/blog/bigdata-deep-dive/04-athena-execution.svg)

四个步骤：
1. **解析 SQL**——生成逻辑计划
2. **查询 Glue Catalog**——获取表定义、分区列表、文件位置
3. **分区裁剪 + 谓词下推**——精确确定需要读取哪些文件和哪些列
4. **并行的 Trino worker 读取 S3**——聚合结果——将输出写入 S3——返回

整个过程对你是透明的：你提交 SQL，拿回结果。AWS 把 Trino 集群完全隐藏在幕后。

### 计费模型

**按扫描的字节数计费**（在 us-east-1 约为 $5/TB）。理解这一点是控制成本的关键。

「扫描的字节数」指的是**从 S3 读取的 Parquet 数据**，而不是结果集的大小：

```sql
-- 假设 ods_event 总共 1 TB
SELECT COUNT(*) FROM ods_event;                          -- scans 1 TB → $5
SELECT COUNT(*) FROM ods_event WHERE dt='2026-05-10';    -- scans 30 GB (one day) → $0.15
SELECT user_id  FROM ods_event WHERE dt='2026-05-10';    -- scans 3 GB (one day + one column) → $0.015
```

**头两个数量级的成本节省来自数据布局**——这正是为什么 Parquet + 分区 + Iceberg 是一套必备组合。

### 性能优化技巧（按收益排序）

| # | 技巧 | 节省 |
|---|---|---|
| 1 | 将 CSV/JSON 转为 Parquet | 70-90% |
| 2 | 按 dt 分区并使用 WHERE dt='...' | 90%+ |
| 3 | 谓词下推（利用 Parquet 列统计信息 / Iceberg） | 50-90% |
| 4 | 合理设置文件大小（128-512 MB） | 减少约 30% 的元数据开销 |
| 5 | 使用 Iceberg 而非 Hive 表 | 更精确的文件级跳过 |
| 6 | LIMIT + 列裁剪（用 `SELECT col1, col2` 而非 `SELECT *`） | 50-90% |
| 7 | 在 Workgroup 中设置最大扫描量限制 | 防止失控查询 |

### Workgroup（工作组）

Workgroup 是 Athena 内部的「配置容器」，它把以下内容打包在一起：

| 设置项 | 用途 |
|---|---|
| **引擎版本** | v2 / v3（完整支持 Iceberg + 更好的性能需要 v3） |
| **结果位置** | 查询结果写入哪个 S3 桶 |
| **加密** | 结果的加密配置 |
| **成本限制** | 每条查询的最大扫描量 / 每个 Workgroup 的最大扫描量 |
| **数据用量控制** | 阈值告警 |
| **标签（Tags）** | 用于成本分摊 |

**生产最佳实践**：每个团队或业务单元一个 Workgroup，从而实现：
- 账单拆分（CloudWatch 按 Workgroup 出报表）
- 成本治理（设置上限以防止昂贵的失控查询）
- 引擎隔离（某些团队仍在用 v2，而不影响其他团队）

### Provisioned Capacity（预置容量，2023 年起）

普通的 Athena 是无服务器的，但有时业务需要严格的 SLA（例如一个必须在 5 秒内返回的 BI 看板——按需模式偶尔会排队）。

**Athena Provisioned Capacity**：预先购买专用的计算单元（DPU），以保证稳定的延迟。
- 最少 24 个 DPU（约 $20/小时），按小时计费
- 适用于对 SLA 有严格要求的工作负载；常规分析并不需要它

### Athena 也能写：CTAS 与 INSERT INTO

Athena 并非只读——它也可以写入数据：

```sql
-- CREATE TABLE AS SELECT
CREATE TABLE dwd_user_action
WITH (
  format = 'PARQUET',
  partitioned_by = ARRAY['dt'],
  location = 's3://my-bucket/warehouse/dwd/user_action/'
) AS
SELECT * FROM ods_event WHERE event_type = 'click';

-- Insert into a new partition
INSERT INTO dwd_user_action
SELECT * FROM ods_event WHERE dt = '2026-05-10';

-- Iceberg table UPDATE / DELETE / MERGE (v3 supports this)
MERGE INTO dwd_user_action t USING staging s
ON t.event_id = s.event_id ...
```

这就是为什么轻量级的 ODS / DWD / DWS / ADS 分层转换最好交给 **Athena CTAS**（最便宜的选项），而重型处理则交给 EMR / Glue Spark。

**官方文档**：
- [Athena 文档](https://docs.aws.amazon.com/athena/)
- [性能调优](https://docs.aws.amazon.com/athena/latest/ug/performance-tuning.html)
- [定价](https://aws.amazon.com/athena/pricing/)
- [查询 Iceberg 表](https://docs.aws.amazon.com/athena/latest/ug/querying-iceberg.html)

---

## Athena 与其他查询引擎的对比

### Athena vs. Redshift

| | Athena | Redshift |
|---|---|---|
| 架构 | 无服务器，存算分离（数据在 S3） | 传统 MPP，存算耦合 |
| 数据位置 | S3（属于你） | Redshift 自己的专用存储 |
| 计费 | 按扫描字节数 | 按节点 / 按小时 |
| 并发 | 默认 25（可调） | 50+（配合 Concurrency Scaling） |
| 性能 | 中（典型 4-30 秒） | 高（亚秒级到几秒） |
| 最适用于 | 数据湖临时查询 / ETL / ML 数据提取 | 高并发的 BI 看板 |

**如何选择**：我们的架构走的是**数据湖路线**，以 Athena 作为主引擎。如果你还需要高并发 BI（例如 100 名分析师同时刷新看板），可以**在上层叠加 Redshift Spectrum**（用 Redshift 的引擎查询 S3 外部表），实现「热数据在 Redshift，冷数据在 S3」的策略。

### Athena vs. EMR Spark

| | Athena | EMR Spark |
|---|---|---|
| 编程方式 | 仅 SQL | SQL + Python + Scala + UDF |
| 复杂度 | 低 | 中到高 |
| 最适用于 | 简单的转换 / 聚合 / 任何 SQL 能表达的操作 | 重型 ETL / ML / 复杂逻辑 |
| 性能 | Trino 在中小数据集上很快 | Spark 在 TB 级以上数据集上更稳定 |

**实践中**：Athena CTAS 处理简单的 SQL 作业；EMR Serverless 处理复杂的 Spark 作业；两者共享同一个 Glue Catalog。

---

## Lake Formation：数据治理与细粒度权限

### 为什么你需要它

Catalog 解决了「这是什么表」的问题，但**「谁能看到哪些列、哪些行」** 需要一个专门的权限层。

举例来说：
- ML 团队需要访问 `ads_user_features`，但**绝不能看到 phone 或 id_card 列**
- BI 分析师只能看到自己所在区域的数据
- 审计人员可以读取所有数据，但不能修改任何内容

传统数据仓库（如 Redshift）有自己的权限系统。但在数据分散于 S3 的数据湖中，你要如何管理访问权限？答案是 **Lake Formation**。

### 什么是 Lake Formation？

**Lake Formation** 是 AWS 构建在 Glue Catalog 之上的细粒度权限管理层：
- 行级安全（Row-Level Security）
- 列级安全（Column-Level Security）
- 数据脱敏 / 基于标签的访问控制（TBAC）
- 跨账户数据共享

### 工作原理

```
传统模型（仅 IAM）：
  IAM Role → S3 bucket policy → 这个 role 能读取 s3://bucket/path 吗？

Lake Formation 模型：
  IAM Role
    → Glue Catalog（Table 概念）
      → Lake Formation 检查：这个 Role 对 ods_user 的特定列是否有 SELECT 权限？
        → 拒绝某些列 / 行，或整体拒绝
```

### 示例：列级权限

Lake Formation 的列级授权**不是标准 ANSI SQL**——它通过三种方式进行配置：

**方式一：LF 控制台 / API（生产环境推荐）**

在 Lake Formation 控制台中：Data permissions，Grant。选择 IAM Role + 表 + **勾选可见的列（include columns）或排除敏感列（exclude columns）**。等价的 CLI：

```bash
aws lakeformation grant-permissions \
  --principal DataLakePrincipalIdentifier=arn:aws:iam::123:role/data-science-role \
  --permissions SELECT \
  --resource '{
    "TableWithColumns": {
      "DatabaseName": "poc_social_layla",
      "Name": "ads_user_features",
      "ColumnNames": ["user_id","age","city","tags"]
    }
  }'
```

**方式二：Athena 的 LF 风格 GRANT（引擎 v3 + 由 LF 管理的表）**

```sql
GRANT SELECT (user_id, age, city, tags)
ON poc_social_layla.ads_user_features
TO PRINCIPAL 'arn:aws:iam::123:role/data-science-role';
```

注意 `TO PRINCIPAL '<完整 ARN>'`——这**不是** PostgreSQL/Redshift 风格的 `TO ROLE 'name'`。

**效果**：

```sql
SELECT * FROM ads_user_features;                    -- Denied (includes phone/id_card)
SELECT user_id, age, tags FROM ads_user_features;   -- OK
```

**方式三：LF 基于标签的访问控制（LF-TBAC）**：给表/列打标签（例如 `pii=true`），然后按标签授予权限。对大型组织而言更具可扩展性。

### 常见坑

Lake Formation 的配置很复杂。常见的痛点：
- 权限叠加在 IAM 之上——调试时需要检查两层
- 由 Glue Crawler 自动创建的表可能不会继承 LF 权限
- 跨账户共享需要 Resource Links 才能生效

**实用建议**：在 POC 阶段，跳过 Lake Formation（使用粗粒度的 IAM）。等到进入生产环境时再启用它。

**官方文档**：[Lake Formation](https://docs.aws.amazon.com/lake-formation/)

---

## 元数据 + 查询的全景图

```
┌─────────────────────────────────────────────────────┐
│              S3 物理数据层                            │
│   warehouse/ods/event/dt=2026-05-10/*.parquet       │
└──────────────────────┬──────────────────────────────┘
                       ▲
                       │ 文件级访问
                       │
┌──────────────────────┴──────────────────────────────┐
│            Glue Data Catalog（元数据）               │
│   poc_social_layla.ods_event                        │
│     schema、分区、location → s3://...               │
└──────────────────────┬──────────────────────────────┘
                       ▲
                       │ 获取表定义
                       │
┌──────────────────────┴──────────────────────────────┐
│       Lake Formation（权限层，可选）                 │
│   针对每个 IAM Role：哪些列 / 行可见                │
└──────────────────────┬──────────────────────────────┘
                       ▲
        ┌──────────────┼─────────────┬─────────────┐
        │              │             │             │
   Athena (SQL)    EMR Spark    SageMaker    Redshift Spectrum
```

---

## 本章小结

| 概念 | 一句话总结 |
|---|---|
| Glue Data Catalog | 数据湖的中央表元数据存储，被所有引擎共享 |
| Glue Crawler | 扫描 S3 并自动建表（生产环境需谨慎使用） |
| Iceberg + Catalog | Catalog 存储的是指向 Iceberg metadata.json 的指针 |
| Athena | 无服务器 SQL 引擎，由 Trino 驱动，按扫描字节数计费 |
| Workgroup | Athena 的配置容器，用于成本治理和团队隔离 |
| Lake Formation | 列级 / 行级权限管理——在 IAM 之上的细粒度访问控制 |

---

## 常见问题

### 什么是 AWS Glue Data Catalog，它为什么重要？

Glue Data Catalog 是数据湖中所有表的集中式元数据存储。每一个引擎——Athena、EMR、Redshift Spectrum、SageMaker——都从同一个目录读取表定义，从而实现共享的湖仓（lakehouse）体验。

### Athena 的计费方式是怎样的，我该如何降低成本？

Athena 按扫描的数据量收费，每 TB 5 美元。使用分区裁剪（WHERE dt=...）、Parquet 等列式格式，以及 Iceberg 的文件级 min/max 统计信息来最小化扫描的数据量，从而降低账单。


---

## 参考资料

- [AWS Glue Developer Guide](https://docs.aws.amazon.com/glue/latest/dg/what-is-glue.html) — AWS Documentation
- [Trino documentation](https://trino.io/docs/current/) — Trino
- [Amazon Athena User Guide](https://docs.aws.amazon.com/athena/latest/ug/what-is.html) — AWS Documentation
