2. 元数据模型设计实战
元数据模型是血缘与 Data Catalog 的“地基”。如果模型只按某一种采集源来设计,后续接入新系统时就会不断加字段、加表、加特例,最终查询血缘时需要在多张表之间反复拼接,性能与可维护性都会迅速恶化。本节的目标是设计一套可扩展的实体-关系模型,让业务元数据、技术元数据和操作元数据在同一套骨架下统一存储,同时保留各自差异。
三类元数据的边界与职责
业务元数据回答“这个数据是什么、归谁管、怎么用”,典型对象包括业务域、数据产品、指标、术语、负责人、SLA 等级。技术元数据回答“数据存在哪里、长什么样、怎么产生”,典型对象包括数据源、库、表、字段、分区、视图、任务、脚本、存储格式。操作元数据回答“数据在什么时候、以什么状态、经过什么过程发生变化”,典型对象包括任务实例、运行状态、开始/结束时间、产出分区、行数、耗时、失败原因、重跑次数。
三类元数据不是三套独立系统,而是同一实体在不同视角下的属性集合。例如一张 Hive 表,技术视角关心它的字段类型与存储路径,业务视角关心它属于哪个主题域、对应哪个指标,操作视角关心它每天由哪个任务实例产出、最近一次产出是否成功。模型设计的关键,是让这三类信息能挂到同一个实体上,而不是各建一张表再靠名称字符串关联。
核心实体与关系骨架
推荐以“资产实体(Asset)”作为统一抽象,用 asset_type 区分 TABLE、FIELD、JOB、DASHBOARD、METRIC 等类型。所有实体共享一张主表,差异属性放入扩展属性表或 JSON 列。这样血缘查询只需围绕 asset_id 展开,不必为每种类型写一套 JOIN。
关系层是血缘的核心。建议至少定义三类边:包含关系(库包含表、表包含字段)、血缘关系(字段级或表级的数据流向)、归属关系(资产归属业务域、负责人、任务)。边表统一使用 src_asset_id、dst_asset_id、relation_type、confidence、source 字段,便于按类型过滤和按来源追溯。
可扩展性的三个设计要点
第一,统一主键与命名空间。所有实体使用全局唯一的 asset_id,推荐格式为 系统:类型:全限定名,例如 hive:table:dw.dwd_order_detail、airflow:job:etl_order_daily。这样即使不同系统出现同名表,也不会冲突,血缘边可以直接引用 ID,避免运行期再做名称解析。
第二,属性分层存储。高频查询字段(名称、类型、负责人、状态)放主表并建索引;低频、易变的属性放扩展表或 JSON 列。例如字段的 description、tags、sample_values 可以放扩展表,避免主表过宽影响扫描性能。
第三,关系边带来源与置信度。血缘可能来自 SQL 解析、任务调度日志、人工登记三种渠道,质量不同。边表增加 source(sql_parse / runtime / manual)和 confidence(0~1),查询时可以按置信度过滤,也可以在冲突时按来源优先级裁决。
建表示例(以关系型存储为例)
CREATE TABLE meta_asset (
asset_id VARCHAR(512) PRIMARY KEY,
asset_type VARCHAR(32) NOT NULL, -- TABLE/FIELD/JOB/METRIC...
name VARCHAR(256) NOT NULL,
qualified_name VARCHAR(512) NOT NULL,
owner VARCHAR(128),
status VARCHAR(32),
create_time DATETIME,
update_time DATETIME,
ext_json JSON
);
CREATE TABLE meta_relation (
src_asset_id VARCHAR(512) NOT NULL,
dst_asset_id VARCHAR(512) NOT NULL,
relation_type VARCHAR(32) NOT NULL, -- CONTAINS/DERIVES/BELONGS_TO
source VARCHAR(32), -- sql_parse/runtime/manual
confidence DECIMAL(3,2) DEFAULT 1.00,
update_time DATETIME,
PRIMARY KEY (src_asset_id, dst_asset_id, relation_type)
);
CREATE INDEX idx_relation_dst ON meta_relation(dst_asset_id, relation_type);
CREATE INDEX idx_asset_type ON meta_asset(asset_type, qualified_name);
字段级血缘查询时,从目标字段出发沿 meta_relation 反向遍历 DERIVES 边即可得到上游链路;表级血缘则先通过 CONTAINS 边把字段聚合到表,再走 DERIVES。这种“字段为最小粒度、表为聚合视图”的做法,能同时满足精确溯源和快速浏览两种需求。
操作元数据如何挂载
操作元数据不应单独建一套资产体系,而应作为任务实例实体挂到资产上。建议定义 JOB_INSTANCE 类型,通过 BELONGS_TO 边关联到 JOB,再通过 PRODUCES 边关联到它产出的表或分区。这样当用户查看某张表时,可以顺带看到“最近 7 次产出实例的成功率与耗时”,而不需要跨系统查询调度平台。
asset_id 生成规则和 relation_type 枚举,再接入采集器。规则一旦确定,后续新增数据源只需扩展 asset_type 和采集适配器,不必改动血缘查询逻辑。常见误区与判断依据
误区一:用“表名+库名”作为血缘边的连接键。一旦出现跨系统同名表或表重命名,血缘就会断裂。判断依据是:能否在不做名称解析的前提下,仅凭 ID 完成一次三跳以上的血缘遍历。
误区二:把业务属性直接写进技术元数据表。这会导致业务口径变更时污染技术采集结果。判断依据是:业务负责人变更是否需要重新跑技术采集任务;如果不需要,说明分层是合理的。
误区三:操作元数据只存最新状态。血缘复盘和 SLA 分析往往需要历史实例,建议按天分区保留实例表,至少保留 30~90 天,具体按存储成本与审计要求权衡。
完成本节模型设计后,可以用一个最小验证用例检查:选取一张真实表,手工写入它的表实体、两个字段实体、一条字段级 DERIVES 边和一个任务实例,然后执行一次反向血缘查询,确认能返回“目标字段 ← 源字段 ← 源表 ← 产出任务实例”的完整链路。若链路完整且无需字符串匹配,说明模型骨架已经可用。