2、血缘采集技术:SQL解析原理、ETL脚本解析、存储过程血缘提取、日志解析方法

血缘采集,说白了就是搞清楚数据从哪来、经过谁、变成了啥。这是整个数据治理的基石。我做了这么多年血缘分析,最深的体会是:采集不准,后面全是白搭。今天咱们就聊聊血缘采集的四种核心技术,都是我在项目里实打实踩过坑、填过土的经验。

2.1 SQL解析原理:血缘采集的“基本功”

SQL解析是血缘采集最核心的技术。你想想看,数据仓库里90%以上的加工逻辑都是SQL,搞不定SQL解析,血缘分析就无从谈起。

SQL解析的本质,就是把一段SQL文本变成计算机能理解的语法树。我习惯把它分成三步:

  1. 词法分析:把SQL拆成一个个,比如SELECT、FROM、WHERE、表名、字段名。
  2. 语法分析:根据SQL语法规则,把组装成抽象语法树(AST)。
  3. 语义分析:在AST上做类型检查、表名解析、字段绑定。

举个例子,下面这条简单的SQL:

SELECT a.user_id, b.order_amount
FROM dim_user a
JOIN fact_order b ON a.user_id = b.user_id
WHERE b.dt = '2024-01-01'

解析后的AST大概长这样:

核心要点:AST的根节点是Query,下面挂两个子节点——SelectList和FromClause。SelectList里有两个ColumnRef(user_id和order_amount),FromClause里有两个TableRef(dim_user和fact_order)和一个JoinCondition。血缘提取就是遍历这棵树,找到所有ColumnRef和TableRef,建立“读-写”关系。

我在项目中遇到过最头疼的问题,是子查询和CTE。比如:

WITH tmp AS (
    SELECT user_id, SUM(amount) AS total
    FROM orders
    GROUP BY user_id
)
SELECT t.user_id, t.total
FROM tmp t
WHERE t.total > 1000

这种嵌套结构,解析器需要先处理CTE的定义,把tmp当成一个临时表,然后再解析外层查询。如果解析器不支持CTE,血缘关系就会断掉。嗯,这里要注意:市面上很多开源解析器对CTE的支持都不完整,选型时要特别留意。

2.2 ETL脚本解析:从“黑盒”到“白盒”

ETL脚本比SQL复杂得多。它通常包含数据抽取、转换、加载三个环节,而且脚本语言五花八门——Shell、Python、Perl、甚至Java。我见过最离谱的项目,ETL脚本里混着SQL、Python和Shell,还互相调用。

解析ETL脚本的核心思路是:找到数据流动的“管道”。我个人习惯的做法是:

  1. 识别数据源和目标:脚本里哪些是读数据的操作(如JDBC连接、文件读取),哪些是写数据的操作(如INSERT、文件输出)。
  2. 提取中间转换逻辑:脚本里对数据做了哪些处理(如过滤、聚合、字段映射)。
  3. 建立调用链:脚本A的输出是不是脚本B的输入?有没有依赖关系?

避坑指南:我曾经接手过一个项目,ETL脚本里大量使用eval()动态执行代码。这种“动态生成”的逻辑,静态解析根本抓不到。后来我们不得不改用运行时采集——在脚本执行时hook住数据读写API,才把血缘关系补全。所以,如果你们的ETL脚本里有大量动态执行,静态解析基本没戏,得考虑运行时方案。

下面是一个典型的ETL脚本解析示例:

# 伪代码:ETL脚本血缘提取
def extract_etl_lineage(script):
    # 1. 解析数据源
    sources = find_all_read_operations(script)
    # 2. 解析数据目标
    targets = find_all_write_operations(script)
    # 3. 解析转换逻辑
    transforms = find_all_transformations(script)
    # 4. 建立血缘关系
    lineage = build_lineage(sources, transforms, targets)
    return lineage

2.3 存储过程血缘提取:最“难啃”的骨头

存储过程是血缘采集的“硬骨头”。为什么?因为存储过程里什么都有——游标、临时表、动态SQL、异常处理、循环嵌套。我见过一个存储过程写了3000行,里面嵌套了7层循环,还用了动态SQL拼接表名。这种代码,人看都费劲,机器解析就更难了。

存储过程血缘提取的难点主要有三个:

难点 原因 我的应对方法
动态SQL 表名、字段名在运行时才确定 静态解析+运行时补录,双管齐下
临时表 临时表生命周期短,跨会话不可见 跟踪临时表的创建和销毁,建立临时血缘
游标循环 逐行处理,血缘关系是“多对多” 把游标展开成集合操作,再提取血缘

我记得有一次,客户的核心存储过程用了大量动态SQL拼接:

CREATE PROCEDURE sp_dynamic_query(@table_name VARCHAR(100))
AS
BEGIN
    DECLARE @sql NVARCHAR(MAX)
    SET @sql = 'SELECT * FROM ' + @table_name + ' WHERE dt = ''2024-01-01'''
    EXEC sp_executesql @sql
END

这种代码,静态解析只能看到@table_name这个变量,根本不知道实际会查哪个表。我们后来做了个“运行时采集器”,在存储过程执行时拦截sp_executesql的调用,把实际执行的SQL抓出来,再解析血缘。说白了,静态搞不定的,就上动态。

2.4 日志解析方法:最后的“兜底方案”

当SQL解析、ETL脚本解析、存储过程提取都搞不定的时候,日志解析就是最后的“兜底方案”。

日志解析的原理很简单:数据库和ETL工具在执行SQL时,都会记录日志。我们只要解析这些日志,就能还原出数据流动的轨迹。常见的日志源包括:

  • 数据库审计日志:记录所有SQL执行情况,包括查询、插入、更新、删除。
  • ETL工具日志:如Informatica、DataStage、Kettle的执行日志。
  • 应用日志:业务系统打印的数据操作日志。

日志解析的流程一般是:

  1. 日志采集:从各个系统拉取日志文件,或者通过syslog、Kafka实时接收。
  2. 日志解析:把非结构化的日志文本,解析成结构化的“谁、在什么时间、对哪个表、做了什么操作”。
  3. 血缘还原:把解析出来的操作,按时间顺序串联成血缘链路。

注意:日志解析有两个“坑”。第一,日志量巨大——一个中型数仓一天能产生几十GB的审计日志,解析性能是个大问题。第二,日志格式不统一——不同数据库、不同版本的日志格式都不一样,解析器要能适配多种格式。我曾经因为Oracle和MySQL的审计日志格式差异,多花了两周时间做适配。

下面是一个简单的日志解析示例:

# 伪代码:审计日志解析
def parse_audit_log(log_line):
    # 假设日志格式: [2024-01-01 10:00:00] user=app01, sql=SELECT * FROM orders
    timestamp = extract_timestamp(log_line)
    user = extract_user(log_line)
    sql = extract_sql(log_line)
    # 解析SQL,提取表名和操作类型
    tables = parse_sql_tables(sql)
    operation = parse_sql_operation(sql)
    return {
        'timestamp': timestamp,
        'user': user,
        'tables': tables,
        'operation': operation
    }

2.5 四种技术的选型建议

这四种技术不是互斥的,而是互补的。我建议的选型策略是:

场景 推荐技术 原因
纯SQL加工 SQL解析 准确率高,性能好
复杂ETL脚本 ETL脚本解析+日志解析 脚本解析抓静态,日志解析补动态
存储过程为主 存储过程提取+运行时采集 静态+动态结合,覆盖最全
异构系统多 日志解析为主 统一日志格式,降低适配成本

说白了,没有一种技术能解决所有问题。我做了这么多项目,最后都是多种技术组合使用。比如,先用SQL解析处理大部分简单场景,再用存储过程提取处理复杂逻辑,最后用日志解析兜底。这样虽然前期投入大,但血缘的完整性和准确性最高。

2.6 血缘采集技术全景图

下面这张图,是我根据多年经验总结的血缘采集技术全景。它展示了四种技术的定位和关系:

血缘采集技术全景图 数据源(SQL/ETL/存储过程/日志) SQL解析 词法/语法/语义分析 ETL脚本解析 数据源/目标/转换 存储过程提取 动态SQL/临时表/游标 日志解析 审计日志/ETL日志 血缘关系(表级/字段级/作业级) 质量问题溯源 / 影响分析 / 数据治理 核心原则:没有一种技术能覆盖所有场景,需要组合使用 SQL解析 + 存储过程提取 + 日志解析 = 最完整的血缘覆盖

从这张图能看出来,血缘采集不是单一技术能搞定的。我建议团队在落地时,先评估数据源的类型和复杂度,再选择合适的组合方案。比如,如果你们的数仓以Hive SQL为主,那SQL解析就能覆盖80%的场景;如果大量使用存储过程,那存储过程提取和运行时采集就是必选项。