4. 元数据采集与同步:抓工具、定策略
我记得刚开始搞数据湖的时候,大家最头疼的不是存储,而是“我东西放哪了?谁碰过?质量怎么样?”元数据采集,说白了就是给数据湖装上一双眼睛。你看不见它,但它什么都知道。
这一章,咱们就聊聊两个主流的抓取工具——Apache Atlas 和 Marquez,以及一个避不开的话题:增量采集。别着急上代码,先理清逻辑。
4.1 为什么需要元数据采集?
你想想看,一个企业级数据湖里,可能有几百上千张表。没有元数据管理,DBA 查个血缘关系得翻半天脚本,分析师找字段定义全靠口口相传。我个人习惯把元数据采集分为三层:
- 技术元数据:表结构、分区、文件格式、存储路径。
- 操作元数据:ETL 作业运行时间、数据量变化、失败记录。
- 业务元数据:字段业务含义、数据质量规则、数据 owner。
采集工具主要解决“技术元数据”和“操作元数据”的自动化抓取。说白了,就是让机器替人干活。
核心观点:元数据采集不是一次性的“搬砖”工作。它必须形成一个闭环——采集 → 存储 → 消费 → 反馈 → 再采集。没有这个循环,你建的数据湖跟一个黑箱子没区别。
4.2 Apache Atlas:企业级元数据治理“重器”
Atlas 是 Hadoop 生态的老牌选手。它支持 Hook 机制,能自动从 Hive、HBase、Sqoop 等组件抓取元数据。我在项目中遇到过最典型的场景:公司有 200 多个 Hive 表,数据工程师离职后没人知道字段怎么来的。Atlas 一上,血缘图自动生成,谁都不好意思再“埋雷”。
4.2.1 架构与采集流程
Atlas 的架构并不复杂,但组件较多。它的核心是“类型系统 + 图存储”。
# 核心组件
- Atlas Server:元数据存储与索引服务(基于 Titan/JanusGraph + Solr)
- Hook:嵌入到 Hive、HDFS 等组件的采集代理
- Import/Export:批量导入导出元数据(如从 Hive Metastore)
- REST API:供外部系统调用
实际抓取时,Hook 监听到 Hive 创建表、修改分区等操作,生成 Kafka 消息。Atlas Server 消费消息,更新 JanusGraph 中的实体和关系。整个过程对用户透明。
一个简单的 Hook 配置示例:
# hive-site.xml 中添加 Atlas Hook
hive.exec.post.hooks=org.apache.atlas.hive.hook.HiveHook
atlas.cluster.name=primary
atlas.rest.address=http://atlas-server:21000
配置好以后,你执行一个 CREATE TABLE,Atlas 自动捕捉。嗯,这里要注意:Hook 对 DDL 操作敏感,但 DML(如 INSERT)默认不采集。如果你需要追踪数据变化,得配合其他工具。
个人经验:Atlas 的 Hook 在集群规模较大时(比如 500+ 节点)会产生不小的网络压力。我的习惯是只对核心业务表启用 Hook,其他表走定时批量导入。这样可以避免采集风暴。
4.2.2 增量采集怎么做的?
Atlas 本身没有“全量 vs 增量”的开关。它的机制是这样的:
- 实时增量:通过 Hook 监听实时事件(DDL、分区变更),这是最理想的增量模式。
- 定时批量:利用 Import/Export 工具,比如从 Hive Metastore 导出元数据,然后调用 Atlas REST API 写入。你可以写个脚本,只导出最近 24 小时变化的表。
我曾经踩过一个坑:全量导入 Atlas 后,发现有些表因为血缘关系没刷新,导致数据治理报告不准。后来我改成每天凌晨跑一次增量导入,配合 Hook 的实时推送,才算稳定下来。
# 增量导入脚本示例(简化版)
#!/bin/bash
# 只导出昨天有变更的表
hive -e "SHOW TABLES;" | while read table; do
mod_time=$(hdfs dfs -stat "%Y-%m-%d" /user/hive/warehouse/$table)
if [[ "$mod_time" == "$(date -d 'yesterday' '+%Y-%m-%d')" ]]; then
./atlas-import.sh --table $table
fi
done
警告:Atlas 的 Import 工具默认是追加模式,不会自动删除已删除表的元数据。你需要额外写一个清理逻辑,否则元数据仓库里会堆积大量“死”资源。
4.3 Marquez:轻量级血缘追踪利器
跟 Atlas 的“重”不同,Marquez 走的是另一种路线。它专注于“数据集血缘 + 作业运行元数据”,更偏向操作元数据。我是在做实时数据湖项目时接触它的。当时团队嫌弃 Atlas 太重,Marquez 的简单 API 和轻量部署让我们眼前一亮。
4.3.1 核心设计
Marquez 只有一个核心服务,依赖 Postgres 或 MySQL 做存储。它不提供 UI 界面(有单独的 Web 端),主要通过 REST API 交互。采集方式也比 Atlas 灵活——你可以通过编程调用 API,也可以接入 OpenLineage 协议。
说白了,Marquez 更像一个“元数据接收器”,由上游工具(如 Spark、Airflow、dbt)主动推送元数据。你不用操心 Hook 冲突或版本兼容。
# 通过 OpenLineage 向 Marquez 推送一条作业运行记录
POST http://marquez:5000/api/v1/lineage
{
"eventType": "COMPLETE",
"job": {
"namespace": "my-namespace",
"name": "etl_daily_order"
},
"inputs": [{
"namespace": "hdfs",
"name": "/data/orders/raw"
}],
"outputs": [{
"namespace": "hdfs",
"name": "/data/orders/clean"
}],
"run": {
"runId": "run-uuid-123"
}
}
这个 API 调用非常简单。你用任意语言封装一下,就能把 ETL 作业的上下游关系记录得一清二楚。
关键区别:Atlas 主动抓(Push),Marquez 被动收(Pull + Push 混合)。如果你们的作业编排工具已经支持 OpenLineage,Marquez 几乎是开箱即用。
4.3.2 增量采集策略
Marquez 的增量采集天然存在。因为每次作业运行都会生成一个带有 runId 的事件。你只需要在 API 请求中带上时间戳或版本号,Marquez 内部会根据 runId 去重。我个人的习惯是:
- 对于定时 ETL 作业,每次运行都推送一条完整事件,Marquez 会自动覆盖旧版本(根据
runId更新)。 - 对于实时流作业(比如 Kafka → Flink → HDFS),我推荐每个微批次推送一次,但带上批次时间戳。Marquez 的 API 支持按时间范围查询,这样你只拉取最近 N 小时的增量元数据。
# 按时间范围查询增量元数据
GET http://marquez:5000/api/v1/lineage?since=2025-03-01T00:00:00Z&until=2025-03-02T00:00:00Z
Marquez 没有全量导入的概念。它的设计初衷就是“持续流入,按需查询”。如果你想迁移数据,只能通过 API 一条条重播。嗯,这里没有捷径。
避坑指南:我曾经用 Marquez 做一个实时数据湖项目,发现当作业频率很高(每分钟一次)时,Postgres 成了瓶颈。解决方案是给 lineage_events 表加分区,并按天清理 30 天前的历史记录。Marquez 本身不提供自动清理,你得写个定时 SQL。
4.4 Atlas vs Marquez:如何选型?
很多同学问我这两个工具哪个好。我的回答是:没有最好,只有最合适。下面这张表是我根据实际项目经验整理的对比:
| 维度 | Apache Atlas | Marquez |
|---|---|---|
| 部署复杂度 | 重(依赖 HBase/Solr/JanusGraph) | 轻(仅需一个 JAR + 数据库) |
| 增量采集能力 | 基于 Hook 的实时增量 + 定时批量导入 | 基于作业事件的天然增量(每条事件独立) |
| 血缘粒度 | 字段级别(表级、列级) | 数据集级别(表/文件级) |
| 生态集成 | 原生支持 Hive/HBase/Sqoop 等 | OpenLineage 协议(Spark/Airflow/dbt 等) |
| UI 与可视化 | 自带管理界面,血缘图可交互 | 需单独部署 Web 端或接 Grafana |
| 适用场景 | 传统 Hadoop 湖、强治理需求 | 现代数据湖、实时流、轻量需求 |
看到这你可能会想:“我能不能两个都要?”我试过。在同一个项目里,用 Atlas 管理技术元数据(表结构、Hive 分区),用 Marquez 追踪作业血缘。两者之间通过 API 同步核心信息。但代价是运维成本翻倍。如果你团队人力有限,我建议只选一个,把增量采集做深做透。
4.5 增量采集策略的核心要点
不管用 Atlas 还是 Marquez,增量采集共同要面对三个问题。我把它总结成一张思维导图:
简单总结一下增量采集的核心三要素:
- 数据变更检测:靠时间戳对比还是事件订阅?我推荐两种都支持。如果你的源系统有 CDC(变化数据捕获),优先用事件。否则,时间戳配合轮询更可靠。
- 去重与幂等性:同一个元数据事件被重复推送怎么办?必须给每个实体分配唯一 ID,并在存储层做 upsert。Atlas 通过 GUID,Marquez 通过
runId,都是这个道理。 - 存储与定期清理:增量数据积累一个月后,元数据仓库可能膨胀一倍。我一般在 Postgres 里按天分区,再用 crontab 删除 60 天前的分区。Atlas 的 JanusGraph 里,可以用 Solr 的 TTL 机制。
重要:增量采集不是万能的。初次部署时,你仍然需要一次全量导入来“初始化”元数据。我的建议是:先跑一次全量,然后用增量持续补充。全量导入的脚本要保留,以便数据湖重建或迁移时复用。