2. 数据资产元数据采集:元数据模型设计、采集策略与频率、增量与全量采集

元数据采集,说白了就是给数据资产做「人口普查」。你连自己家里有多少张表、字段是什么类型、数据从哪里来都不知道,后面做热度分析就是空中楼阁。我在好几个项目里都见过这种情况——业务方问「这张表到底谁在用?」,平台方答不上来,因为元数据压根没采全。

这一章,我们就来聊聊怎么把元数据「采好」。核心就三件事:模型怎么设计、策略怎么定、增量全量怎么配合。

2.1 元数据模型设计:先搭骨架,再填肉

我个人习惯,设计元数据模型时先想清楚三个层次:

  • 技术元数据:库、表、字段、分区、存储路径、文件格式、数据量。
  • 业务元数据:字段中文名、业务定义、数据域、所属流程、负责人。
  • 操作元数据:ETL 作业、调度时间、血缘关系、访问日志、变更记录。

你想想看,如果只采技术元数据,你只能知道「有张表叫 dwd_order_di」,但不知道它对应哪个业务订单、谁负责维护。反过来,只采业务元数据,你又没法做技术层面的热度计算。所以,三者缺一不可。

下面是我在项目中常用的一套核心模型结构,供你参考:

模型名称 核心字段 说明
catalog_info catalog_id, name, type, owner, create_time 数据目录/库级信息
schema_info schema_id, catalog_id, name, description 模式/命名空间
table_info table_id, schema_id, table_name, table_type, location, format, row_count, size_bytes 表级元数据,核心实体
column_info column_id, table_id, column_name, data_type, comment, is_partition, ordinal_position 字段级元数据
partition_info partition_id, table_id, partition_value, create_time, row_count 分区信息,Hive/Iceberg 等场景必用
etl_job_info job_id, job_name, job_type, schedule_expr, source_table, target_table, last_run_time ETL 作业元数据
lineage_edge edge_id, upstream_table, upstream_column, downstream_table, downstream_column, job_id 血缘关系边
access_log log_id, table_id, user, query_type, query_time, duration_ms, engine 访问日志,热度计算的核心输入

关键原则:模型设计要「够用但不过度」。我曾经在一个项目里把元数据模型拆到了 30 多张表,结果采集链路太长,数据还没采完,业务方已经下班了。后来精简到 8 张核心表,反而跑得又快又稳。

2.2 采集策略与频率:不是所有数据都需要秒级刷新

元数据采集最忌讳「一刀切」。不同元数据的变更频率天差地别——表结构可能一个月改一次,访问日志每秒都在产生。你如果都用同一套频率去采,要么浪费资源,要么数据滞后。

我建议按变更频率把元数据分成三类:

  • 静态元数据(低频变更):库、表、字段结构。这类数据通常只在 DDL 操作时变化,一天采一次全量就够了。
  • 半静态元数据(中频变更):分区信息、数据量、文件数。每次数据写入后都会变,建议每小时或每批次结束后采集。
  • 动态元数据(高频变更):访问日志、查询记录、作业运行状态。这类数据需要实时或准实时采集,延迟控制在分钟级。

下面是我在项目中常用的采集频率配置:

元数据类型 采集频率 采集方式 典型数据源
表结构/字段 每天 1 次(凌晨) 全量 Hive Metastore, MySQL Information Schema
分区信息 每小时 1 次 增量 Hive Metastore, Iceberg/REST Catalog
数据量/行数 每批次结束后 增量 Spark/Hive ANALYZE TABLE, 文件系统
访问日志 实时(Kafka 流式) 增量 Hive/Spark Audit Log, Presto/Trino Query Log
ETL 作业状态 每 5 分钟 增量 调度系统 API (Airflow/DolphinScheduler)
血缘关系 每天 1 次 全量+增量 SQL Parser, Atlas Hook, DataHub

我的经验:访问日志的采集一定要走消息队列,别直接查数据库。我曾经见过有人用 JDBC 轮询 Presto 的查询日志表,结果把 Presto Coordinator 查挂了,整个集群的查询都受影响。后来改成 Flume + Kafka 的流式采集,再也没出过问题。

2.3 增量与全量采集:双剑合璧

全量采集和增量采集不是二选一,而是配合使用。我习惯用一句话概括:「全量保底,增量提效」。

2.3.1 全量采集

全量采集就是把目标数据源的所有元数据全部拉一遍。优点是简单、完整、不会漏;缺点是慢、占资源、对源系统压力大。

适用场景:

  • 首次初始化元数据仓库
  • 数据源结构发生重大变更(如重建表)
  • 增量采集出现数据不一致,需要全量修复

代码示例(伪代码,从 Hive Metastore 全量采集表信息):

def full_collect_tables(metastore_client):
    """全量采集所有表元数据"""
    all_databases = metastore_client.get_all_databases()
    all_tables = []
    for db in all_databases:
        tables = metastore_client.get_all_tables(db)
        for table_name in tables:
            table_info = metastore_client.get_table(db, table_name)
            all_tables.append({
                "database": db,
                "table_name": table_name,
                "create_time": table_info.createTime,
                "last_access_time": table_info.lastAccessTime,
                "table_type": table_info.tableType,
                "location": table_info.sd.location,
                "fields": [{"name": f.name, "type": f.type} for f in table_info.sd.cols]
            })
    return all_tables

2.3.2 增量采集

增量采集只拉取上次采集后发生变化的数据。优点是快、轻量、对源系统友好;缺点是需要可靠的变更捕获机制。

常见的增量采集方式:

  • 时间戳对比:利用表的 create_time、update_time 字段,只拉取最近变更的数据。
  • 事件监听:通过 Hook 或 Listener 捕获 DDL/DML 事件,实时推送变更。
  • 日志解析:解析数据库的 Binlog、Audit Log,提取元数据变更。
  • 版本号对比:利用元数据存储的版本号或序列号,只拉取高版本的数据。

代码示例(基于时间戳的增量采集):

def incremental_collect_tables(metastore_client, last_collect_time):
    """增量采集最近变更的表"""
    # 假设 Metastore 支持按时间过滤
    changed_tables = metastore_client.get_tables_changed_since(last_collect_time)
    incremental_data = []
    for table_ref in changed_tables:
        table_info = metastore_client.get_table(table_ref.db, table_ref.name)
        incremental_data.append({
            "database": table_ref.db,
            "table_name": table_ref.name,
            "change_type": table_ref.change_type,  # CREATE / ALTER / DROP
            "change_time": table_ref.change_time,
            "current_schema": [{"name": f.name, "type": f.type} for f in table_info.sd.cols]
        })
    return incremental_data

避坑指南:我曾经在一个项目里只用增量采集,结果某个表被重建了(DROP + CREATE),时间戳没变,增量采集完全没感知到。从那以后,我定了一个规矩:每周至少跑一次全量采集做数据对账,发现不一致就触发全量修复。

2.4 整体采集架构

下面这张图是我在项目中常用的元数据采集架构,你可以参考:

元数据采集架构总览 数据源层 Hive Metastore MySQL Information Schema 调度系统 API 查询引擎 Audit Log 采集层 全量采集器 (每日) 增量采集器 (实时/定时) 事件监听器 (Hook) 日志解析器 (Flink) 消息队列 / 缓冲区 Kafka Topic: metadata_change_events 元数据存储层 元数据关系库 (MySQL/PostgreSQL) 搜索引擎 (Elasticsearch) 应用层 数据地图 / 目录 热度分析引擎 血缘可视化

嗯,这张图基本概括了元数据采集的全链路。从数据源出发,经过全量/增量/事件/日志四种采集方式,统一进入 Kafka 消息队列,然后分别写入关系库(用于精确查询)和搜索引擎(用于全文检索和快速过滤),最后供给上层应用使用。

核心要点总结:

  • 元数据模型要覆盖技术、业务、操作三个维度,8 张核心表足够起步
  • 采集频率按变更频率分级,静态一天一次,动态实时采集
  • 全量采集用于初始化和对账,增量采集用于日常变更捕获
  • 访问日志必须走流式采集,别用 JDBC 轮询
  • 每周至少一次全量对账,防止增量采集漏数据