3. 血缘分析的三大技术流派:解析式、日志式、标记式

做数据血缘分析,说白了就是搞清楚数据「从哪来、到哪去、中间经历了什么」。但实现这个目标的路子,业内主要有三条。我这些年踩过的坑,多半都跟选错路子有关。

今天咱们就把这三个流派掰开揉碎,看看它们各自怎么玩,有什么坑,适合什么场景。

一句话总结:解析式靠「读代码」,日志式靠「看记录」,标记式靠「贴标签」。没有银弹,只有最合适的组合。

3.1 解析式(SQL Parser)—— 最正统,但也最「娇气」

解析式,顾名思义,就是直接解析 SQL 语句。它把 SQL 文本拆成抽象语法树(AST),然后从树里提取表、字段、函数之间的依赖关系。

我个人习惯用这种流派做核心系统的血缘。为什么?因为它准。只要 SQL 能解析成功,血缘关系就是确定的,不需要猜。

3.1.1 工作原理

-- 示例:一条简单的 ETL SQL
INSERT INTO dws.daily_sales
SELECT
    order_date,
    product_id,
    SUM(amount) AS total_amount
FROM ods.orders
WHERE status = 'completed'
GROUP BY order_date, product_id;

-- 解析器会提取出:
-- 目标表:dws.daily_sales(字段:order_date, product_id, total_amount)
-- 源表:ods.orders(字段:order_date, product_id, amount)
-- 过滤条件:status = 'completed'
-- 聚合操作:SUM(amount)

解析器内部会经历:词法分析 → 语法分析 → AST 构建 → 血缘提取。每一步都可能出问题。

3.1.2 优点与缺点

维度 说明
精度 极高。字段级血缘可以精确到列,甚至能识别表达式中的中间字段
实时性 好。SQL 提交就能解析,不需要等任务跑完
依赖 强。需要完整的 SQL 文本,且 SQL 必须符合语法规范
维护成本 高。不同方言(Hive SQL、Spark SQL、MySQL)需要不同的解析器

我曾经踩过的坑:有一次解析 Presto 的 SQL,里面用了大量的 WITH 子句和 LATERAL VIEW,解析器直接崩了。后来发现是 Presto 的语法扩展没覆盖到。所以,解析式对 SQL 方言的兼容性要求极高,别指望一个解析器打天下。

3.1.3 适用场景

  • 数据仓库 ETL 任务:SQL 规范,变更频率低,适合做精确血缘
  • 数据建模工具:如 dbt、DataForm,天然有 SQL 文本,解析成本低
  • 数据质量监控:需要精确知道某个字段的来源,做影响分析

3.2 日志式(任务日志)—— 最通用,但最「粗糙」

日志式不碰 SQL,它只读任务运行日志。比如 Spark 的 Event Log、Hive 的 Job Log、Airflow 的 Task Log。从日志里提取「谁读了什么表、写了什么表」。

你想想看,很多老系统根本没有 SQL 文本,或者 SQL 被封装在存储过程里,解析器根本拿不到。这时候日志式就派上用场了。

3.2.1 工作原理

# 模拟一条 Spark 日志中的血缘信息
# 原始日志片段:
# 2024-01-15 10:23:45 INFO SparkContext: 
#   Registered input table: ods.orders (format: parquet)
#   Registered output table: dws.daily_sales (format: parquet)
#   Shuffle read: 2.3 GB
#   Shuffle write: 1.1 GB

# 日志解析器会提取:
# 输入:ods.orders
# 输出:dws.daily_sales
# 数据量级:2.3 GB → 1.1 GB(可推算压缩比)

日志式不关心 SQL 怎么写,只关心「实际发生了什么」。所以它天然支持所有计算引擎,只要你能拿到日志。

3.2.2 优点与缺点

维度 说明
通用性 极强。Spark、Flink、Hive、MapReduce 都能用
精度 低。通常只能到表级,很难到字段级
实时性 差。必须等任务跑完才能拿到日志
存储成本 高。日志量大,长期存储需要规划

我的建议:日志式最适合做「血缘发现」—— 先通过日志把表级关系摸清楚,再针对关键链路用解析式做字段级细化。别一上来就想字段级,容易把自己累死。

3.2.3 适用场景

  • 历史系统迁移:老系统没有 SQL 文档,日志是唯一线索
  • 多引擎混用环境:Spark 做批处理、Flink 做流处理、Hive 做查询,统一用日志收口
  • 数据资产盘点:先摸清有哪些表、谁在用,再决定要不要做精细血缘

3.3 标记式(数据打标)—— 最灵活,但最「人工」

标记式,说白了就是在数据产生的时候,主动打上「我是谁、我从哪来」的标签。比如在数据写入时,额外写一条元数据记录:「这条数据来自 ods.orders 的 order_id 字段」。

我记得有一次帮一家金融公司做血缘,他们的核心系统是 Cobol 写的,SQL 解析和日志解析都行不通。最后我们用了标记式,在数据接入层手动打标,反而效果最好。

3.3.1 工作原理

-- 标记式示例:在数据写入时主动记录血缘
-- 假设有一个血缘记录表 lineage_tags
INSERT INTO lineage_tags (
    target_table,
    target_field,
    source_table,
    source_field,
    transform_rule,
    etl_time
) VALUES (
    'dws.daily_sales',
    'total_amount',
    'ods.orders',
    'amount',
    'SUM(amount) WHERE status = ''completed''',
    CURRENT_TIMESTAMP
);

-- 数据本身不需要变,血缘信息单独存储

标记式的核心是「约定大于配置」。你需要定义一套标签规范,然后让开发者在写 ETL 时顺手打标。这听起来麻烦,但一旦形成习惯,血缘的准确率是最高的。

3.3.2 优点与缺点

维度 说明
精度 最高。只要打标规范,字段级、甚至记录级血缘都能实现
灵活性 最强。支持任意数据源、任意转换逻辑
人工成本 高。需要开发者配合,且容易遗漏
一致性 差。不同团队打标规范可能不一致

我曾经踩过的坑:有次团队为了省事,让开发者在代码里硬编码血缘标签。结果三个月后,代码重构了,标签没更新,血缘全乱了。标记式必须配套自动化校验机制,比如定期对比标签和实际 SQL 是否一致。

3.3.3 适用场景

  • 非 SQL 数据源:如 API 接口、文件导入、消息队列
  • 复杂转换逻辑:比如 Python 脚本里做了多层 DataFrame 操作,解析器和日志都搞不定
  • 合规审计场景:需要精确到某条数据来自哪个上游系统,标记式最可靠

3.4 三大流派对比总览

为了方便你选型,我把三个流派的核心差异整理成了一张表。嗯,这张表我每次做咨询都会拿出来用。

维度 解析式 日志式 标记式
血缘粒度 字段级 表级(少数可到字段) 字段级/记录级
准确率 高(语法正确时) 中(依赖日志完整性) 高(依赖打标规范)
实时性 高(提交即解析) 低(任务完成后) 中(写入时打标)
维护成本 高(方言适配) 中(日志格式变化) 高(规范管理)
适用引擎 SQL 类引擎 所有引擎 所有引擎
推荐场景 核心数仓 ETL 血缘发现/盘点 合规审计/非 SQL 源

3.5 流派选择框架图

下面这张图是我自己总结的选型逻辑。你可以根据你的实际情况,沿着箭头找到最适合的流派。

血缘分析流派选择框架 开始选型 有完整的 SQL 文本吗? 是 → 解析式(SQL Parser) 否 → 继续判断 能获取任务运行日志吗? 是 → 日志式(任务日志) 否 → 标记式(数据打标) 选型建议 • 解析式:适合 SQL 规范、变更可控的核心数仓,精度最高 • 日志式:适合多引擎混用、历史系统,先做表级血缘发现 • 标记式:适合非 SQL 源、合规审计,人工成本高但最灵活 • 实际项目中,通常是「解析式 + 日志式」组合使用

3.6 我的实战建议

做了这么多年血缘,我最大的体会是:别追求一步到位。

我建议你分三步走:

  1. 先用日志式做全量扫描,花一周时间把公司所有任务的血缘摸清楚,哪怕只是表级。这一步能帮你发现 80% 的隐藏依赖。
  2. 再对核心链路用解析式做字段级细化。比如财务报表、用户画像这些关键数据,必须精确到字段。
  3. 最后用标记式补漏。那些解析器和日志都搞不定的「黑盒」任务,手动打标,并配套自动化校验。

一个小技巧:如果你用的是 Spark,可以同时用日志式(读 Event Log)和解析式(读 SQL 文本)。两者交叉验证,准确率能到 99% 以上。我之前的项目就是这么干的,效果出奇的好。

好了,三大流派就聊到这儿。记住,没有最好的流派,只有最适合你当前阶段的流派。选对了,事半功倍;选错了,后面全是坑。