3. 血缘关系的核心要素:节点、边与属性
聊血缘关系,咱们得先搞清楚它到底长什么样。说白了,它就是一张有向图。图里有什么?节点、边,还有附着在上面的属性。这三个东西,缺一个都不完整。
我在项目里见过不少团队,一上来就急着搭血缘平台,结果连「节点」和「边」都没定义清楚。最后查故障时,图是画出来了,但根本没法用。嗯,咱们先把基础打牢。
3.1 节点:血缘中的实体
节点就是图中的「点」。在数据世界里,最常见的节点有三类:表、字段和任务。
3.1.1 表节点
表是血缘关系中最粗粒度的单元。一张表就是一个节点。比如 ods_order、dwd_order_detail、ads_sales_summary,它们各自独立,又通过数据流动连在一起。
我个人习惯,在表节点上至少记录两个属性:存储引擎和数据量级。为什么?有一次排查一个报表延迟问题,我盯着血缘图看了半天,发现 ads_sales_summary 的上游表 dwd_order_detail 是 Hive 表,数据量 10 亿行,而下游任务却只给了 2 个 Map 的资源。这就是典型的「表节点属性缺失」导致的盲区。
表节点核心属性清单
- 表名(全限定名,如
project.dataset.table) - 存储引擎(Hive / HBase / ClickHouse / MySQL)
- 数据量级(行数、存储大小)
- 分区信息(分区字段、分区数)
- 生命周期(TTL、保留天数)
3.1.2 字段节点
字段节点是更细粒度的实体。它描述的是「列」级别的血缘。比如 order_id 从 ods_order 流到 dwd_order_detail,再到 ads_sales_summary 的 order_cnt。
字段级别的血缘,在故障排查中特别有用。我记得有一次,业务方反馈某个报表的「订单金额」字段突然变成 0。我顺着字段血缘一路回溯,发现 ads_sales_summary.amount 的上游字段 dwd_order_detail.pay_amount 在三天前被一个 ETL 任务改成了 COALESCE(pay_amount, 0)。而那个字段原本是 pay_amount,不允许为 NULL。改完之后,所有 NULL 都被转成了 0,但上游的过滤条件没同步更新,导致一批有效数据被误判为 0。
你看,如果没有字段级血缘,这种问题你根本不知道从哪查起。
我的建议:字段节点一定要记录「数据类型」和「空值约束」。这两个属性在字段变更时,能帮你快速判断影响范围。
3.1.3 任务节点
任务节点是「动作」的载体。它描述的是数据是怎么从上游流到下游的。常见的任务节点包括:
- ETL 任务:如 Spark 作业、Hive SQL、DataFlow 管道
- 数据同步任务:如 Canal、DataX、Flume
- 数据服务任务:如 API 接口、报表查询
任务节点上,我建议至少记录:调度时间、执行状态、运行时长。为什么?因为故障往往不是「数据错了」,而是「数据没按时到」。你想想看,一个任务延迟了 10 分钟,下游 20 个任务全部等待,最终导致报表延迟 2 小时。这时候,任务节点的「运行时长」属性就是排查的关键线索。
3.2 边:依赖关系的载体
边就是节点之间的「线」。它描述的是数据流动的方向和依赖关系。边分为两种:数据流边和调度依赖边。
3.2.1 数据流边
数据流边表示「数据从 A 流到了 B」。比如 ods_order → dwd_order_detail,表示 dwd_order_detail 的数据来源于 ods_order。
数据流边通常带有「转换逻辑」属性。比如:
-- 数据流边:ods_order → dwd_order_detail
-- 转换逻辑:
INSERT OVERWRITE TABLE dwd_order_detail
SELECT order_id, user_id, pay_amount, create_time
FROM ods_order
WHERE dt = '${yesterday}'
AND status = 'paid';
这个转换逻辑,在故障排查时就是「铁证」。我曾经遇到一个场景:业务方说某个字段值不对,我打开数据流边上的 SQL,发现 WHERE 条件里多了一个 AND channel = 'app'。这个条件把 PC 端的数据全过滤掉了。如果没有这个转换逻辑,你根本不知道数据是在哪一步被「篡改」的。
3.2.2 调度依赖边
调度依赖边表示「任务 B 必须在任务 A 完成后才能启动」。它不描述数据流向,只描述执行顺序。
举个例子:
Task_A (ods_order_etl)→Task_B (dwd_order_etl)Task_B (dwd_order_etl)→Task_C (ads_sales_etl)
调度依赖边在故障排查中,主要用来分析「连锁延迟」。如果 Task_A 失败了,Task_B 和 Task_C 都会被阻塞。这时候,你只需要看调度依赖边的「状态」属性,就能快速定位阻塞点。
注意:数据流边和调度依赖边不要混为一谈。有些工具把两者合并了,但我在实践中发现,分开记录更有利于排查。数据流边告诉你「数据怎么变的」,调度依赖边告诉你「任务为什么没跑」。
3.3 属性:让血缘活起来
节点和边是骨架,属性才是血肉。没有属性的血缘图,就是一张死图。我个人认为,以下三个属性是必须的:时间戳、版本、状态。
3.3.1 时间戳
时间戳记录的是「什么时候发生的」。节点上的时间戳包括:
- 创建时间:表或字段是什么时候创建的
- 最后修改时间:表结构或字段定义是什么时候变更的
- 数据更新时间:表里的数据是什么时候刷新的
边上的时间戳包括:
- 调度时间:任务计划在什么时间执行
- 实际执行时间:任务实际开始和结束的时间
时间戳在故障排查中的作用,我举个例子你就明白了。有一次,一个报表在早上 8 点准时刷新,但数据却是前天的。我查了血缘图,发现 ads_sales_summary 的上游表 dwd_order_detail 的数据更新时间是「昨天 23:00」,而 dwd_order_detail 的上游 ods_order 的数据更新时间是「前天 22:00」。问题出在哪?ods_order 的数据同步任务在昨天挂了,导致 dwd_order_detail 刷新的数据还是前天的。如果没有时间戳,你根本不知道数据「有多旧」。
3.3.2 版本
版本属性用来追踪「变更历史」。表结构会变,字段定义会变,任务逻辑也会变。每一次变更,都应该生成一个新版本。
我建议用 递增整数 或 时间戳 作为版本号。比如:
-- 表 dwd_order_detail 的版本历史
-- v1 (2024-01-01): 初始版本,包含 order_id, user_id, pay_amount
-- v2 (2024-03-15): 新增字段 channel, 修改 pay_amount 类型为 DECIMAL(18,2)
-- v3 (2024-06-01): 删除字段 old_status, 新增字段 status
版本属性在故障排查中,主要用于「回滚分析」。我记得有一次,一个字段突然报错,说类型不匹配。我查了字段的版本历史,发现三天前有人把 pay_amount 从 INT 改成了 STRING,但下游的报表任务没更新。回滚到 v1 版本后,问题立刻消失。
3.3.3 状态
状态属性描述的是「当前是什么情况」。节点和边都应该有状态。
| 实体 | 状态值 | 含义 |
|---|---|---|
| 表节点 | ACTIVE / DEPRECATED / DELETED | 表是否在用、已废弃、已删除 |
| 字段节点 | ACTIVE / DEPRECATED / RENAMED | 字段是否在用、已废弃、已重命名 |
| 任务节点 | RUNNING / SUCCESS / FAILED / SKIPPED | 任务当前执行状态 |
| 数据流边 | ENABLED / DISABLED / BROKEN | 数据流是否正常、被禁用、断裂 |
| 调度依赖边 | WAITING / SATISFIED / TIMEOUT | 依赖条件是否满足 |
状态属性在故障排查中,是「第一眼信息」。你打开血缘图,看到某个任务节点的状态是 FAILED,或者某条数据流边的状态是 BROKEN,问题范围立刻就缩小了。
3.4 核心要素关系图
下面这张图,把节点、边、属性的关系串起来了。你可以把它当作血缘关系的「最小知识框架」。
3.5 实战:用核心要素定位故障
理论说完了,咱们看一个真实案例。假设你收到报警:「ads_sales_summary 报表数据为空」。你怎么用血缘关系排查?
- 查状态:打开血缘图,看
ads_sales_summary的上游任务节点状态。发现dwd_order_etl的状态是FAILED。 - 查时间戳:看
dwd_order_etl的实际执行时间,发现它从昨天 23:00 开始跑,跑了 2 小时还没结束,最终超时失败。 - 查版本:看
dwd_order_etl的版本历史,发现三天前有人更新了任务逻辑,把JOIN条件改成了LEFT JOIN,导致数据量暴增。 - 查数据流边:看
ods_order→dwd_order_detail的数据流边,发现转换逻辑里多了一个GROUP BY操作,导致数据被聚合,下游拿不到明细。
你看,从状态到时间戳,再到版本和数据流边,每一步都在缩小问题范围。这就是血缘关系核心要素的价值。
我的经验:在搭建血缘系统时,先把节点、边、属性的模型定义清楚。不要贪多,先把「表、字段、任务」三个节点,「数据流、调度依赖」两条边,「时间戳、版本、状态」三个属性做扎实。后面再扩展,就水到渠成了。