订阅更新

一份数据被10个下游用,改一处坏全部——数据血缘如何用于质量保障?

数据质量手册 第 18 题 约 10 分钟读完

本文目录
  1. 先说结论
  2. 血缘的三层用途详解
  3. 血缘的三个粒度
  4. 血缘怎么采集?
  5. 血缘的工程化挑战
  6. OpenLineage:血缘标准化
  7. 面试高频追问
  8. 实战踩坑:血缘信息不准导致的影响分析失效
  9. 总结

先说结论

数据血缘三大用途:①影响分析(变更前预判下游影响);②根因定位(数据出问题反查上游);③质量传播(上游质量问题传染下游)。

血缘的层次:表级血缘(dwd_a → dws_b)→ 字段级血缘(dwd_a.col1 → dws_b.col2)→ 行级血缘(哪些行影响了下游)。粒度越细价值越大但采集成本越高,生产实践中字段级最实用。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

「我改一下dwd_user表的某字段类型,应该没什么影响吧?」——结果第二天下游15张表挂了、3个BI报表打不开、2个推荐模型预测失准。这种「不知道改东西会影响谁」的痛,每个数据团队都经历过。数据血缘(Data Lineage)就是解药。这道题考的是「如何把血缘从展示工具变成质量保障工具」。我在出海硬件公司搭过一套用血缘做影响分析的体系,今天讲怎么落地。

血缘的三层用途详解

用途一:变更影响分析(预防故障)

-- 我要修改 dwd_user.email 字段类型,先看影响哪里
WITH RECURSIVE downstream AS (
  SELECT downstream_col AS col, downstream_table AS tbl, 1 AS depth
  FROM column_lineage
  WHERE upstream_table = 'dwd_user' AND upstream_col = 'email'
  UNION ALL
  SELECT cl.downstream_col, cl.downstream_table, d.depth + 1
  FROM column_lineage cl JOIN downstream d
    ON cl.upstream_table = d.tbl AND cl.upstream_col = d.col
  WHERE d.depth < 5
)
SELECT tbl, col, depth, owner
FROM downstream JOIN table_meta ON tbl = table_name
ORDER BY depth, owner;
-- 一个查询返回:所有受影响的下游表/字段/owner
-- 变更前钉钉所有owner,确认OK再改

用途二:根因定位(事后追溯)

-- ads_user_funnel某指标突然异常,怎么找根因?
-- 沿血缘从下往上追
WITH RECURSIVE upstream AS (
  SELECT upstream_table AS tbl, 1 AS depth
  FROM table_lineage WHERE downstream_table = 'ads_user_funnel'
  UNION ALL
  SELECT tl.upstream_table, u.depth + 1
  FROM table_lineage tl JOIN upstream u ON tl.downstream_table = u.tbl
  WHERE u.depth < 10
)
SELECT u.tbl, u.depth,
       (SELECT MAX(dt) FROM partition_status WHERE table_name = u.tbl) AS latest_dt,
       (SELECT status FROM dq_results WHERE table_name = u.tbl AND dt = '${bizdate}') AS dq_status
FROM upstream u
ORDER BY depth;
-- 查看上游每张表的最新分区时间和DQC状态
-- 哪张表DQ异常或分区延迟,就是根因

用途三:质量传播(标记传染表)

# 当某张表DQ失败时,自动标记下游为「待重算」
def propagate_dq_failure(failed_table, dt):
    downstream = bfs_downstream(failed_table)
    for tbl in downstream:
        # 标记下游同分区为「质量未确认」
        mark_partition_status(tbl, dt, 'PENDING_RECHECK')
        # 阻断下游消费
        block_downstream_consumers(tbl, dt)
        # 通知owner
        notify_owner(tbl, 
          f'上游{failed_table}.{dt} DQ失败,本表已标记待重算')

血缘的三个粒度

粒度描述采集难度用途
表级dwd_a → dws_b低(解析SQL FROM/INSERT)影响范围、依赖关系
字段级dwd_a.col1 → dws_b.col2中(解析SELECT列)字段变更影响、字段级DQ传播
行级哪些行→哪些行极高(运行时追踪)数据修复、合规追溯

生产实践:字段级血缘是性价比最高的。表级太粗(改某个不重要的字段不该惊动所有下游),行级太细(采集成本指数级)。我在出海硬件公司用的就是字段级,覆盖核心ETL任务。

血缘怎么采集?

血缘采集有三种主流方案:

# 方案1: SQL解析(事前/静态)
# 用sqlparser/Apache Calcite解析SQL AST
from sqlglot import parse_one, exp
 
sql = "INSERT INTO dws_user SELECT u.id, u.email FROM dwd_user u WHERE u.dt='...'"
ast = parse_one(sql)
 
# 提取目标表
target = ast.find(exp.Insert).this.sql()  # dws_user
# 提取源表和字段
for col in ast.find_all(exp.Column):
    print(col.table, col.name)  # dwd_user, id / dwd_user, email
 
# 方案2: 运行时采集(事中/动态)
# 通过Hook介入Spark/Hive执行计划
# 准确但侵入性高
 
# 方案3: 调度系统注入(事后/被动)
# DataWorks/DolphinScheduler/Airflow解析任务依赖
# 快但只能拿到表级

我推荐组合:SQL解析(字段级)+ 调度系统(任务级依赖)。这样既能字段级精确,又能补充非SQL任务(Python脚本、API调用)。

血缘的工程化挑战

挑战表现应对
动态SQLSELECT * / EXEC动态拼全量列出所有字段+定期schema对账
跨引擎Spark+Flink+Trino混合统一血缘标准(OpenLineage)
UDF不透明用户自定义函数没法解析标记UDF依赖整列
视图/CTE嵌套层级太深难展开递归展开+缓存
旧任务无记录存量任务没血缘从历史日志逆向工程

OpenLineage:血缘标准化

OpenLineage是Linux Foundation下的开源项目,定义了跨引擎统一的血缘元数据标准。Spark、Flink、Airflow、dbt都有官方Integration。它解决了「不同引擎血缘格式不一」的问题。

# OpenLineage事件示例(Spark任务执行时自动发出)
{
  "eventType": "COMPLETE",
  "job": {"namespace": "datawarehouse", "name": "dwd_user_etl"},
  "inputs": [
    {"namespace": "hive", "name": "ods_user",
     "facets": {"schema": {"fields": [...]}}}
  ],
  "outputs": [
    {"namespace": "hive", "name": "dwd_user",
     "facets": {"columnLineage": {
       "fields": {
         "email": {"inputFields": [{"namespace":"hive","name":"ods_user","field":"email"}]}
       }
     }}}
  ]
}

我在当前出海短剧公司推OpenLineage后,把Spark/Flink/dbt的血缘统一到一个图里,实现了真正的跨引擎追踪。

面试高频追问

追问一:血缘和元数据是一回事吗?

血缘是元数据的子集。元数据是「描述数据的数据」(含schema、owner、SLA、标签等),血缘专门描述「数据流向关系」。一般在元数据平台(DataHub、Atlas、Datawise)里,血缘是其中一个核心模块。

追问二:血缘图太复杂看不懂,怎么办?

三个简化策略:①分层——只看ods/dwd/dws/ads特定层级,不要全展开;②分场景——影响分析只看下游、根因定位只看上游、不需要双向;③业务标签——给关键节点打「核心」「财务」「合规」标签,过滤显示。我在出海硬件公司的血缘平台默认只展示3层、加场景过滤、按owner着色,可读性大幅提升。

追问三:血缘平台怎么落地推动?

血缘平台单独建意义不大,必须接入实际工作流。三个高价值场景:①每次PR review必须看血缘图,把影响分析嵌入开发流程;②故障复盘标准模板里强制要血缘图;③SLA违约要追溯血缘。把血缘从「展示工具」变成「工作工具」,使用率才上得来。我们出海硬件公司血缘平台MAU能保持在数据团队的80%+。

实战踩坑:血缘信息不准导致的影响分析失效

出海硬件公司有次我要改dwd_order的某字段,看血缘平台显示「下游3张表」,钉钉了owner,确认ok就改了。结果上线后炸了——实际下游有10张表,血缘平台只采集到了3张。

根因:剩下7张表是通过Python脚本而不是SQL从dwd_order查数据的,血缘解析器只解析SQL,Python代码里的`spark.sql("select ...")`没解析到。

解决方案:做了两件事:

1)血缘补全机制——除了SQL解析,还订阅Spark的Catalyst事件,运行时捕获实际访问的表

2)血缘自检——每周对比血缘图和实际访问日志(Hive Hook、HoloAccess Log),差异>5%告警,强制人工核查

# 血缘自检脚本
lineage_edges = load_lineage_edges()  # 平台记录的边
actual_edges = parse_query_logs(last_7_days)  # 实际访问日志
 
missing = actual_edges - lineage_edges
if len(missing) / len(actual_edges) > 0.05:
    alert('Lineage coverage below 95%, missing edges: ', missing)

教训:血缘的准确性比覆盖度更重要。不准的血缘会给人虚假的安全感,比没血缘还危险。生产实践里必须有自检机制——血缘图必须每周对账,覆盖率<95%或差异>5%必须人工核查。

总结

血缘三大用途(影响分析/根因定位/质量传播)+ 三层粒度(表/字段/行)+ 三种采集方式(SQL解析/运行时/调度)。OpenLineage是标准化方向。准确性比覆盖度更重要。面试时按用途+粒度+采集+不准事故讲,体现工程深度。

下一篇聊一个让告警真正有效的话题:数据质量报警太多没人看——告警降噪与分级响应。

你们血缘平台用什么?覆盖度多少?评论区聊聊~

关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长

刷题进度保存在当前浏览器