数据血缘与影响分析:从字段链路到变更治理和故障定位

文章来源声明: 原文作者:大大大大晴天; 来源站点:掘金; 原文链接:https://juejin.cn/post/7689029864910356530; 本文基于上述来源整理/加工,觅优补充点评,仅供技术学习交流。版权归原作者所有。
觅优短评

本文适合数据治理、数仓研发与SRE团队。核心价值是把血缘从静态图谱推进到变更评估与故障定位闭环,帮助降低上线风险、缩短排障路径。

一、引言 ----

数据质量告警经常给出的是“结果异常”,例如ads_order_daily.pay_amt环比下降 40%、某个字段空值率突增、分区产出延迟、指标口径不一致。但真正需要处理的是后续三个问题:异常从哪里来、影响到哪些报表或服务、修复或变更会不会引入新的问题。

在大数据治理体系里,数据质量负责发现“哪里不对”,数据血缘负责回答“为什么不对、影响到谁、改动会波及哪里”。如果只有质量规则告警,却没有血缘关系,排障通常会停留在人工翻 SQL、查调度、问业务的阶段;而当表级、字段级、任务级血缘被系统化采集并形成图谱后,影响分析、链路追踪、变更评估和故障定位就可以从经验判断变成可查询、可解释、可复用的工程能力。

质量发现问题,血缘缩小问题空间。没有血缘时,排障依赖人脑记忆;有血缘时,排障可以从异常字段出发,沿上游找来源,沿下游看影响,结合任务运行状态定位故障点。

二、血缘的三层模型

表级、字段级、任务级血缘解决的是不同粒度的问题。工程落地时不能只做其中一层,否则能力会断裂。

一个简化后的三层血缘图如下:

表级血缘知道ads_order_daily 依赖哪些表;字段级血缘知道pay_amt 、user_cnt 、pay_order_cnt 分别来自哪些字段和表达式;任务级血缘知道这些表由哪些任务产出,以及哪一次运行可能失败或延迟。

三、表级血缘采集

表级血缘是最容易落地、收益也最直观的一层。它的基本结构是:

upstream_dataset  ──[produced/consumed by job]──▶ downstream_dataset

常见采集方式可以分为四类:

表级血缘的采集流程通常如下:

<span>             表级血缘采集流程
</span>
  ┌────────────┐
  │ SQL / 配置 │
  └─────┬──────┘
<span>        │
        ▼
  ┌────────────┐       ┌────────────┐
  │ 解析输入表 │       │ 解析输出表 │
  └─────┬──────┘       └─────┬──────┘
        │                    │
        └────────┬───────────┘
                 ▼
          ┌────────────┐
          │ 规范化命名 │
          └─────┬──────┘
                │
                ▼
          ┌────────────┐
          │ 生成边关系 │
          └─────┬──────┘
                │
                ▼
          ┌────────────┐
          │ 写入图存储 │
          └────────────┘
</span>

这里最容易被低估的是“规范化命名”。如果一个平台把表写成db.table ,另一个平台写成hive://cluster/db/table ,再一个平台写成catalog.db.table ,最后得到的血缘会被拆成多个孤立节点。工程实践中要先建立统一的资产 URN 或 qualified name 规则,再谈血缘准确率。

四、字段级血缘解析

字段级血缘的价值在于解释“某个指标为什么变成这样”。比如ads_order_daily.pay_amt 异常,表级血缘只能告诉你它来自dwd_order_detail 和dws_user_order ,字段级血缘才能进一步说明它来自SUM(pay_amt) ,并受过滤条件status='paid' 、分区条件和币种转换逻辑影响。

以一段 SQL 为例:

<span>CREATE</span> <span>TABLE</span> ads_order_daily <span>AS</span>
<span>SELECT</span>
    dt,
    <span>COUNT</span>(<span>DISTINCT</span> user_id) <span>AS</span> pay_user_cnt,
    <span>SUM</span>(pay_amt) <span>AS</span> pay_amt
<span>FROM</span> dwd_order_detail
<span>WHERE</span> status <span>=</span> <span>'paid'</span>
<span>GROUP</span> <span>BY</span> dt;

字段级血缘可以抽象为:

字段级血缘解析示例

dwd_order_detail.dt       ───────────────▶ ads_order_daily.dt
dwd_order_detail.user_id  ──COUNT <span>DISTINCT</span>▶ ads_order_daily.pay_user_cnt
dwd_order_detail.pay_amt  ───────SUM─────▶ ads_order_daily.pay_amt
dwd_order_detail.status   ───<span>FILTER</span>──────▶ ads_order_daily.pay_user_cnt
dwd_order_detail.status   ───<span>FILTER</span>──────▶ ads_order_daily.pay_amt

这里有一个关键判断:status 虽然没有出现在 SELECT 输出字段中,但它影响了结果集范围,因此应该作为条件依赖进入字段级血缘。否则当上游status 枚举变更时,系统无法发现它会影响下游支付指标。

字段级解析一般要经过以下步骤:

               字段级血缘解析流程

SQL 文本
  │
  ▼
词法 / 语法解析
  │
  ▼
AST 抽象语法树
  │
  ▼
表别名解析、CTE 展开、子查询展开
  │
  ▼
SELECT / JOIN / WHERE / GROUP BY / HAVING 语义分析
  │
  ▼
字段依赖边生成
  │
  ├── 直接映射:<span>a</span><span>.id</span> -> <span>b</span><span>.id</span>
  ├── 表达式映射:<span>a</span><span>.price</span> * <span>a</span><span>.num</span> -> <span>b</span><span>.amount</span>
  ├── 聚合映射:SUM(<span>a</span><span>.amount</span>) -> <span>b</span><span>.total_amount</span>
  └── 条件依赖:<span>a</span><span>.status</span> -> <span>b</span><span>.total_amount</span>

字段级血缘的难点不在于简单 SQL,而在于复杂工程场景:

字段级血缘不能盲目追求“100% 自动准确”,更稳妥的策略是给每条字段边记录来源与置信度。

五、任务级血缘采集

任务级血缘把“数据如何流动”和“作业如何运行”连接起来。表级和字段级血缘偏静态,任务级血缘偏运行态,它回答的是:哪一个任务在什么时候消费了哪些数据、产出了哪些数据、是否失败、失败原因是什么、它的父任务或上游调度实例是谁。

任务级血缘可以建模为:

                 任务级血缘事件模型

        ┌──────────────┐
        │   DataJob    │
        │ dwd_order_etl│
        └──────┬───────┘
               │ has run
               ▼
        ┌──────────────┐
        │    Run       │
        │ <span>run_id</span>=xxx   │
        │ <span>state</span>=FAILED │
        └───┬──────┬───┘
            │      │
      reads │      │ writes
            ▼      ▼
   ┌───────────┐  ┌──────────────┐
   │ ods_order │  │ dwd_order    │
   └───────────┘  └──────────────┘
            │
            ▼
   error_message / start_time / end_time / attempt / owner

任务级血缘在故障定位中尤其重要。假设ads_order_daily 质量告警,系统可以沿表级血缘向上查找dws_user_order ,再通过任务级血缘找到最近一次产出该表的任务运行,进而读取状态、日志地址、错误信息和上游任务状态。这样排障路径就从“人工查一圈”变成“沿图查询”。

质量告警驱动的故障定位路径

<span>[质量告警]</span>
 ads_order_daily<span>.pay_amt</span> 环比下降 <span>40%</span>
        │
        ▼
<span>[字段级血缘]</span>
 pay_amt <- <span>SUM</span>(dws_user_order.pay_amt)
        │
        ▼
<span>[表级血缘]</span>
 dws_user_order <- dwd_order_detail <- ods_order
        │
        ▼
<span>[任务级血缘]</span>
 job_dwd_to_dws 最近一次运行延迟 <span>38</span> 分钟
        │
        ▼
<span>[定位结论]</span>
 上游 dwd_order_detail 分区迟到,导致 dws 聚合输入不完整

这类能力的前提是任务运行、数据产出、质量结果必须共享一套资产标识。如果质量系统用ads_order_daily ,调度系统用project.ads_order_daily ,血缘系统用hive.prod.ads_order_daily ,三者无法自动关联,排障体验会明显下降。

六、采集架构设计

一套可维护的血缘平台通常由采集层、解析层、标准化层、存储层和应用层组成。

采集架构要重点解决四个工程问题。

第一,采集要分静态和动态。静态血缘来自 SQL、配置、代码仓库、dbt manifest 等,适合提前做变更评估;动态血缘来自运行事件、执行计划、Listener、Hook 等,适合还原真实运行链路。

第二,血缘边要保存来源。比如同一条A -> B 的边,可能来自 SQL 解析,也可能来自运行时事件。前者代表“设计上依赖”,后者代表“实际运行时依赖”。两者同时存在时可信度更高;两者冲突时,反而能暴露配置漂移、动态分支或采集缺口。

第三,图存储要支持多跳遍历。DataHub 的血缘读取接口支持 upstream/downstream 方向查询,并可通过max_hops 获取多跳上下游;字段级血缘结果中还包含路径信息。血缘存储不能只服务“直接上下游”,还要支持“从一个字段向下追 3 跳”“找出所有经过某张表的链路”这类查询。

第四,血缘要能版本化。一次 SQL 改动可能改变字段计算逻辑,表结构变更也可能改变下游影响范围。如果只保存最新图,事后复盘很难回答“昨天为什么没问题,今天为什么有问题”。血缘版本可以按任务版本、SQL hash、Schema 版本、运行时间窗口来组织。

七、影响分析

影响分析的目标是回答:如果改动一个表、字段、任务或口径,会影响哪些下游资产和业务。

典型输入包括:

影响分析可以从下游遍历开始:

字段变更影响分析

源字段:dwd<span>_order_</span>detail.pay<span>_amt
        │
        ▼
直接影响:
  dws_</span>user<span>_order.pay_</span>amt
  dws<span>_shop_</span>order.pay<span>_amt
        │
        ▼
二跳影响:
  ads_</span>order<span>_daily.pay_</span>amt
  ads<span>_shop_</span>daily.gmv
<span>        │
        ▼
业务影响:
  经营日报
  店铺分析看板
  GMV 预警规则
</span>

工程上建议把影响分为三类:

这类分级很重要。否则一次上游字段改动可能推送几百个下游对象,最后用户会忽略所有通知。

八、链路追踪

链路追踪关注的是“数据从源头到结果经过了哪些节点”。它与影响分析方向相反:影响分析多从上游向下游扩散,链路追踪常从结果向上游回溯。

指标链路追踪:ads_order_daily.pay_user_cnt

ads_order_daily.pay_user_cnt
        ▲
        │ COUNT <span>DISTINCT</span> user_id
        │
dws_user_order.user_id
        ▲
        │ 聚合用户订单
        │
dwd_order_detail.user_id
        ▲
        │ 清洗订单明细
        │
ods_order.user_id
        ▲
        │ Kafka / Binlog 入湖
        │
mysql.<span>order</span>.user_id

链路追踪不只是展示路径,还要在路径上附加关键上下文:

这使链路追踪从“画图”变成“排障证据链”。当用户查看某个指标时,系统不仅能展示上游表,还能回答“这个字段是聚合来的、过滤条件是什么、最近哪层开始异常”。

九、变更评估

变更评估最好发生在上线之前,而不是事故之后。一个成熟的流程应该在 SQL 合并、任务发布、字段变更、表下线之前自动触发血缘分析。

变更评估流程

开发提交 SQL / 配置变更
<span>        │
        ▼
解析新旧血缘
        │
        ▼
对比血缘差异
        │
        ├── 新增上游表
        ├── 删除上游字段
        ├── 输出字段表达式变化
        ├── 下游影响范围变化
        └── 敏感字段传播变化
        │
        ▼
生成评估结果
        │
        ├── 可直接发布
        ├── 需要负责人确认
        └── 阻断发布
</span>

变更评估要特别关注字段级变化。例如下面这个改动看似只是优化 SQL,但会改变指标口径:

变更前:
<span>SUM</span>(pay_amt) <span>AS</span> gmv

变更后:
<span>SUM</span>(<span>CASE</span> <span>WHEN</span> status <span>=</span> <span>'paid'</span> <span>THEN</span> pay_amt <span>ELSE</span> <span>0</span> <span>END</span>) <span>AS</span> gmv

如果没有字段级和条件依赖血缘,系统只能看到输出字段仍然是gmv ,很难发现口径已经变化。更进一步,如果status 字段被标记为枚举字段,变更评估还应该检查枚举值是否稳定。

十、故障定位

故障定位通常从告警开始,但不能停留在告警本身。一个推荐的定位顺序是:

故障定位四步

<span>1.</span> 定位异常对象
   表 / 字段 / 分区 / 指标

<span>2.</span> 查找最近变化
   数据质量结果 / Schema / SQL / 任务版本 / 调度时间

<span>3.</span> 沿血缘回溯
   字段级优先,表级补充,任务级验证

<span>4.</span> 收敛根因
   上游数据异常 / 任务失败 / 延迟产出 / 口径变更 / Schema 漂移

示例:

告警:
ads_order_daily.pay_amt 今日 <span>10</span><span>:</span><span>00</span> 分区金额下降 <span>40</span>%

血缘回溯:
ads_order_daily.pay_amt
  <span><-</span> dws_user_order.pay_amt
  <span><-</span> dwd_order_detail.pay_amt
  <span><-</span> ods_order.pay_amt

任务状态:
job_ods_to_dwd   COMPLETE  <span>09</span><span>:</span><span>05</span>
job_dwd_to_dws   COMPLETE  <span>09</span><span>:</span><span>40</span>
job_dws_to_ads   COMPLETE  <span>10</span><span>:</span><span>02</span>

质量结果:
ods_order.pay_amt       正常
dwd_order_detail.pay_amt 正常
dws_user_order.pay_amt   异常

定位:
异常首次出现在 dws_user_order 层,优先检查 job_dwd_to_dws 的 SQL、分区过滤和聚合逻辑。

如果任务级血缘采集了错误信息,定位还能更快。

十一、落地建议

血缘系统最容易失败的原因不是图画得不漂亮,而是准确率、覆盖率和使用场景没有闭环。建议按以下顺序推进。

第一阶段先做表级血缘,把核心数仓链路接起来。重点不是覆盖所有系统,而是覆盖高价值链路,例如 ODS-DWD-DWS-ADS、核心报表、核心指标和高频变更任务。此阶段的目标是支持基本上下游查询和表下线影响分析。

第二阶段补字段级血缘,优先覆盖核心指标字段。字段级血缘成本更高,不适合一开始追求全域覆盖。建议从财务、交易、用户、风控等关键主题域开始,把高价值字段的计算链路解析清楚。

第三阶段接入任务级运行信息,把血缘和调度、质量、告警打通。表和字段解释“依赖关系”,任务运行解释“本次为什么异常”。只有三者结合,才能支撑故障定位闭环。

第四阶段引入变更评估,把血缘能力前移到研发流程。上线前自动比较新旧血缘、识别下游影响、标记敏感字段传播、通知资产负责人,能显著降低变更事故。

可以用一个成熟度模型描述:

血缘能力成熟度

L0  无血缘
<span>    靠人工查 SQL 和问人
</span>
L1  表级血缘
<span>    能查上下游表,支持基础影响分析
</span>
L2  字段级血缘
<span>    能解释指标字段来源,支持口径核验
</span>
L3  任务级血缘
<span>    能关联运行状态,支持故障定位
</span>
L4  治理闭环
<span>    质量、血缘、变更、权限、告警联动
</span>