2. 数据血缘与元数据
聊数据版本控制,绕不开两个基础概念:数据血缘和元数据。
说白了,数据血缘就是数据的「族谱」。它记录数据从哪来、经过哪些加工、最终去了哪。而元数据,就是数据的「身份证信息」——描述数据的数据。
我刚开始做数据平台时,觉得这两个东西可有可无。直到有一次线上事故,某个报表字段突然变成空值,我花了整整两天才找到根因——一个上游ETL脚本改了字段名,下游没人知道。嗯,从那以后,我再也不敢忽视血缘和元数据了。
什么是数据血缘
数据血缘(Data Lineage),描述的是数据从产生到消亡的完整生命周期。它像一张有向图,每个节点代表一个数据实体(表、字段、文件),每条边代表一次数据流转或变换。
核心价值:出了问题,你能顺着血缘图快速定位「罪魁祸首」。而不是像无头苍蝇一样,到处翻代码、问同事。
举个例子。你看到一张报表上的「当日销售额」是 100 万。这个数字怎么来的?
- 原始数据来自订单系统的
order表 - 经过 Flink 实时清洗,写入
dwd_order明细表 - 再聚合到
dws_sales_daily日汇总表 - 最后被 BI 工具读取展示
这就是一条简单的血缘链路。每个环节都记录了「谁、什么时间、用什么逻辑、处理了什么数据」。
血缘的粒度
我个人习惯把血缘分成三个粒度:
| 粒度 | 描述 | 典型场景 |
|---|---|---|
| 表级血缘 | 表与表之间的依赖关系 | A表 → B表 → C表 |
| 字段级血缘 | 字段级别的映射与变换 | order.amount → dws.total_amount |
| 脚本级血缘 | SQL/代码级别的数据流 | INSERT INTO ... SELECT ... FROM ... |
你想想看,如果只有表级血缘,你只知道「A表影响了B表」。但字段级血缘能告诉你「A表的amount字段,经过SUM()聚合后,变成了B表的total_amount」。排查问题时,后者显然更有用。
元数据管理的基础
元数据,就是「关于数据的数据」。它回答三个问题:
- 有什么数据?——数据目录、表名、字段名
- 数据长什么样?——数据类型、长度、约束、样本值
- 数据怎么来的?——血缘关系、加工逻辑、更新时间
我在项目中遇到过一种典型情况:业务方问「用户画像表里有个user_level字段,这个值是怎么算的?」。如果没有元数据管理,你得去翻代码、问开发、查文档。有了元数据,直接查字段描述和血缘就能回答。
元数据的分类
业内一般把元数据分成三类:
| 类型 | 内容 | 举例 |
|---|---|---|
| 技术元数据 | 数据库、表结构、字段类型、分区信息、ETL脚本 | CREATE TABLE user (id INT, name STRING) |
| 业务元数据 | 业务定义、指标口径、数据质量规则 | 「活跃用户」定义为近30天有登录行为的用户 |
| 管理元数据 | 数据负责人、创建时间、访问权限、版本号 | 表 owner、数据更新频率、SLA |
三者缺一不可。技术元数据告诉你「怎么用」,业务元数据告诉你「什么意思」,管理元数据告诉你「谁负责」。
一个小技巧:我习惯在元数据系统里给每个字段加一个「口径说明」字段。哪怕只有一句话,也能省去大量沟通成本。比如「gmv = 订单金额 - 退款金额,不含运费」。
血缘与元数据的关系
血缘是元数据的一种特殊形式。它描述的是数据之间的动态关系,而不是静态属性。
你可以这样理解:
- 元数据是「名词」——描述数据是什么
- 血缘是「动词」——描述数据怎么流动、怎么变换
没有元数据,血缘图上的节点就是一堆「无名氏」。没有血缘,元数据只能告诉你「有什么」,不能告诉你「从哪来、到哪去」。
数据版本控制中的血缘应用
在数据版本控制场景里,血缘的作用尤其明显。我举个例子:
- 你发布了一个新版本的数据处理脚本
- 新版本改了某个字段的计算逻辑
- 下游有 20 张报表依赖这个字段
如果没有血缘,你根本不知道哪些报表会受影响。有了血缘,系统自动列出所有下游依赖,你就能提前通知业务方,或者做兼容处理。
我曾经踩过的坑:有一次我改了上游一个维表的字段类型,从 STRING 改成 INT。我以为只有两张表引用它。结果上线后,下游三个实时任务直接报错。后来一查血缘图,发现还有一条隐藏的依赖链路——通过一个中间表间接引用了。从那以后,我每次改表结构,必先查血缘图。
数据血缘的核心结构
下面这张图展示了典型的数据血缘关系:
元数据管理的关键能力
一个成熟的元数据管理系统,至少需要具备以下能力:
- 自动采集:从数据源(Hive、MySQL、Kafka等)自动抓取元数据,不需要人工录入
- 血缘解析:解析SQL脚本,自动构建字段级血缘关系
- 版本管理:记录元数据的变更历史,支持回溯
- 搜索与发现:支持按表名、字段名、业务关键词搜索
- 影响分析:输入一个表或字段,列出所有下游依赖
我建议:刚开始做元数据管理时,别追求大而全。先搞定技术元数据自动采集和表级血缘。跑通之后,再逐步加字段级血缘和业务元数据。一步到位往往意味着一步都到不了。
血缘解析的常见方式
解析血缘关系,主要有三种方式:
| 方式 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 静态解析 | 解析SQL AST(抽象语法树) | 无需运行,速度快 | 无法处理动态SQL、UDF |
| 运行时捕获 | 拦截执行计划,记录输入输出 | 准确度高,覆盖所有场景 | 有性能开销,需要侵入系统 |
| 日志解析 | 分析数据库审计日志 | 无侵入,适合已有系统 | 解析复杂,实时性差 |
我个人习惯用「静态解析 + 运行时捕获」的组合。静态解析覆盖 80% 的常规场景,运行时捕获兜底剩下的 20%。
一个简单的血缘解析示例
假设你有这样一条 SQL:
INSERT INTO dws.daily_sales
SELECT
order_date,
SUM(amount) AS total_amount
FROM dwd.orders
WHERE status = 'paid'
GROUP BY order_date;
解析器会提取出:
- 目标表:
dws.daily_sales - 源表:
dwd.orders - 字段映射:
dwd.orders.amount→dws.daily_sales.total_amount - 过滤条件:
status = 'paid' - 聚合操作:
SUM()
这些信息会被存入元数据库,供后续查询。
注意:字段级血缘解析对复杂 SQL(子查询、CTE、窗口函数)的准确率不是 100%。我建议在解析结果上增加人工校验入口,允许数据工程师修正血缘关系。别问我怎么知道的——我被不准的血缘图坑过不止一次。
元数据与版本控制的结合
数据版本控制离不开元数据。每次数据变更,元数据系统都应该记录:
- 变更了什么(表结构、字段逻辑、分区策略)
- 谁变更的
- 什么时间变更的
- 变更前后的版本号
- 受影响的上下游
有了这些信息,你才能做到「一键回滚」和「影响分析」。否则,版本控制就是空谈。
好了,这一节的内容就这些。记住两句话:血缘告诉你数据怎么来的,元数据告诉你数据是什么。两者结合,才是数据版本控制的基石。