3. 血缘关系建模:表级、字段级、过程级与存储结构
血缘分析这件事,做浅了就是画个线,做深了就是搭骨架。我见过不少团队,一上来就追求字段级血缘,结果数据链路一复杂,图根本跑不动。反过来,只做表级血缘又太粗糙,根本定位不到具体问题。
所以这一章,咱们把血缘的「粒度」和「存法」彻底聊透。说白了,就是回答三个问题:
- 血缘到底分几层?
- 每层能解决什么问题?
- 存到哪儿最合适?
3.1 表级血缘:最基础的「谁依赖谁」
表级血缘,就是看表与表之间的上下游关系。比如 ods_order → dwd_order_detail → dws_order_summary。它不关心字段,只关心表。
我在项目中遇到过,有一次数据质量告警说「订单金额对不上」。我第一反应不是查字段,而是先拉出表级血缘图,看看哪些表引用了 dwd_order_detail。结果发现一张被遗忘的 tmp_order_backup 表还在跑旧逻辑。你看,表级血缘在「影响分析」场景下特别管用。
- 快速定位数据链路断裂点
- 评估表变更的影响范围
- 辅助数据资产盘点
存储上,表级血缘最简单。关系型数据库里一张 table_lineage 表就够了:
CREATE TABLE table_lineage (
id BIGINT PRIMARY KEY,
source_table VARCHAR(255),
target_table VARCHAR(255),
etl_job_id VARCHAR(100),
created_at TIMESTAMP
);
嗯,这里要注意:表级血缘虽然简单,但别只存「直接依赖」。我建议把「传递依赖」也预计算出来,否则做影响分析时要递归查很多层,性能扛不住。
3.2 字段级血缘:精细到「列对列」
字段级血缘,才是真正能帮我们修数据质量的利器。它告诉你 dwd_order.amount 是从 ods_order.amount 直接映射过来的,还是经过 price * quantity 计算得来的。
举个例子。有一次我发现 dws_report.revenue 比财务系统少了 0.5%。查字段级血缘发现,中间有一层做了 ROUND(amount, 0),把小数截掉了。这种问题,表级血缘根本看不出来。
SELECT 子句中的表达式、函数、子查询。我建议先用 sqlparse 或 ANTLR 做一层抽象,别自己手写正则。
字段级血缘的存储结构,关系型数据库可以这样设计:
CREATE TABLE field_lineage (
id BIGINT PRIMARY KEY,
source_table VARCHAR(255),
source_field VARCHAR(255),
target_table VARCHAR(255),
target_field VARCHAR(255),
transform_expression TEXT,
etl_job_id VARCHAR(100)
);
但说实话,字段级血缘一旦上了规模,关系型数据库就有点吃力了。一张表几百个字段,上下游链路一多,JOIN 查询慢得让人抓狂。这时候,图数据库的优势就出来了。
3.3 过程级血缘:把「怎么算的」也记下来
过程级血缘,很多人会忽略。它不光记录「谁到谁」,还记录「经过了什么处理」。比如:
- 字段 A 经过
SUM()聚合后变成字段 B - 字段 C 和字段 D 通过
CASE WHEN逻辑生成字段 E - 一张表经过
JOIN、FILTER、UNION后生成新表
我为什么强调过程级血缘?因为数据质量问题的根因,往往藏在「过程」里。字段级血缘告诉你「谁影响了谁」,过程级血缘告诉你「怎么影响的」。比如一个字段值突然变成 NULL,过程级血缘能帮你定位到是哪个 LEFT JOIN 没匹配上,还是哪个 WHERE 条件过滤掉了。
DISTINCT 去重操作导致数据丢失,查了三天才找到。从那以后,我坚持把「过程节点」也建模进去。
过程级血缘的存储,我建议用图数据库。因为过程节点本身就是一种「中间实体」,用关系型数据库存会变成一张巨大的 edge 表,查询效率极低。
3.4 血缘图的存储结构:图数据库 vs 关系型数据库
这是很多团队纠结的问题。我直接说结论:没有银弹,看场景选。
| 对比维度 | 图数据库(Neo4j、JanusGraph) | 关系型数据库(MySQL、PostgreSQL) |
|---|---|---|
| 查询模式 | 深度遍历、路径查询、多跳关联 | 点查、简单 JOIN、聚合统计 |
| 字段级血缘查询 | 快(O(n) 级别) | 慢(需要递归 CTE 或多表 JOIN) |
| 过程级血缘建模 | 天然支持(节点+边) | 需要额外设计中间表 |
| 变更影响分析 | 秒级返回全链路 | 分钟级(数据量大时) |
| 运维成本 | 高(需要专门团队) | 低(现有 DBA 即可) |
| 数据一致性 | 弱(最终一致性) | 强(ACID) |
| 适合场景 | 字段级+过程级血缘、影响分析 | 表级血缘、元数据管理、报表 |
我个人习惯是:表级血缘用关系型数据库,字段级和过程级血缘用图数据库。这样既保证了基础查询的稳定性,又让深度分析有了性能保障。
下面这张图,是我在项目中常用的血缘建模结构:
你看,三层结构各司其职。表级血缘负责「宏观链路」,字段级血缘负责「精准定位」,过程级血缘负责「根因分析」。三者配合,才能把数据质量修复这件事做透。
3.5 图数据库建模实战(以 Neo4j 为例)
如果你决定用图数据库,我建议这样建模:
// 节点类型
(:Table {name: 'ods_order', schema: 'ods', db: 'hive'})
(:Field {name: 'amount', table: 'ods_order', data_type: 'decimal'})
(:Process {type: 'expression', detail: 'price * quantity'})
// 关系类型
(:Field)-[:MAP_TO {transform: 'direct'}]->(:Field)
(:Field)-[:DERIVED_FROM {expression: 'SUM(amount)'}]->(:Field)
(:Table)-[:CONTAINS]->(:Field)
(:Process)-[:INPUT]->(:Field)
(:Process)-[:OUTPUT]->(:Field)
查询「某个字段的上游全链路」:
MATCH (f:Field {name: 'revenue', table: 'dws_report'})
CALL apoc.path.expand(f, 'MAP_TO|DERIVED_FROM|INPUT|OUTPUT', 'Field|Process', 1, 10)
YIELD path
RETURN path
这个查询,在关系型数据库里可能要写几十行递归 CTE,而且性能堪忧。但在 Neo4j 里,毫秒级返回。