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 字段,便于按类型过滤和按来源追溯。

统一元数据实体-关系模型(三类元数据挂载于同一资产骨架) 业务元数据 业务域 / 主题域 指标 / 术语 / 数据产品 负责人 / SLA 等级 回答:是什么、归谁管、怎么用 技术元数据 数据源 / 库 / 表 / 字段 分区 / 视图 / 存储格式 任务 / 脚本 / 字段类型 回答:存在哪里、长什么样、怎么产生 操作元数据 任务实例 / 运行状态 开始/结束时间 / 耗时 产出分区 / 行数 / 失败原因 回答:何时、什么状态、经过什么过程 资产实体 Asset asset_id · asset_type · name · qualified_name owner · status · create_time · update_time 关系边表 Relation Edge 包含:库→表→字段 relation_type = CONTAINS 血缘:源字段→目标字段 relation_type = DERIVES 归属:资产→业务域/任务 relation_type = BELONGS_TO

可扩展性的三个设计要点

第一,统一主键与命名空间。所有实体使用全局唯一的 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 边和一个任务实例,然后执行一次反向血缘查询,确认能返回“目标字段 ← 源字段 ← 源表 ← 产出任务实例”的完整链路。若链路完整且无需字符串匹配,说明模型骨架已经可用。