4. 元数据采集技术:JDBC/ODBC采集、日志解析、API对接、爬虫技术
元数据采集,说白了就是给数据资产做「人口普查」。你连自己家里有多少张表、哪些字段、怎么来的都不知道,后面的治理、血缘、质量全是空谈。我做了这么多年数据架构,见过太多团队一上来就搞数据治理平台,结果卡在采集这一步——不是采不全,就是采不准,最后项目烂尾。
这一章,我把四种主流采集技术掰开揉碎讲清楚。每种技术都有它的脾气,选对了事半功倍,选错了天天加班修管道。
4.1 JDBC/ODBC采集:最传统,也最稳
JDBC和ODBC,是关系型数据库的「标准接口」。你想想看,MySQL、Oracle、SQL Server、PostgreSQL,它们都支持JDBC。所以用JDBC采集元数据,是最通用的方案。
具体怎么干?其实就是连上数据库,执行系统表或信息视图的查询。比如MySQL里查 information_schema.TABLES 和 information_schema.COLUMNS,Oracle里查 ALL_TAB_COLUMNS。
- 表名、表注释、表类型(表/视图)
- 字段名、字段类型、字段长度、是否为空、默认值
- 主键、外键、索引信息
- 分区信息、存储引擎、行数估算
给你看一段我常用的JDBC采集代码(Java版):
// 获取所有表信息
DatabaseMetaData metaData = connection.getMetaData();
ResultSet tables = metaData.getTables(
catalog, schemaPattern, "%", new String[]{"TABLE", "VIEW"});
while (tables.next()) {
String tableName = tables.getString("TABLE_NAME");
String tableType = tables.getString("TABLE_TYPE");
String remarks = tables.getString("REMARKS");
// 获取字段信息
ResultSet columns = metaData.getColumns(
catalog, schemaPattern, tableName, "%");
while (columns.next()) {
String colName = columns.getString("COLUMN_NAME");
int dataType = columns.getInt("DATA_TYPE");
String typeName = columns.getString("TYPE_NAME");
int colSize = columns.getInt("COLUMN_SIZE");
boolean nullable = columns.getInt("NULLABLE") == 1;
}
}
有一次采集Oracle的元数据,getTables() 返回了上万个临时表。原因是schema参数传错了,导致把整个实例的表都扫了一遍。那次采集跑了6个小时,还把源库的CPU打到了90%。后来我加了两层过滤:第一层按schema白名单,第二层按表名正则排除临时表。
JDBC采集的优缺点很明显:
| 优点 | 缺点 |
|---|---|
| 标准化接口,兼容性好 | 对源库有性能影响(尤其大库) |
| 能采集到细粒度的字段级信息 | 无法采集非结构化数据 |
| 实现简单,JDBC驱动现成 | 大数据组件(Hive、HBase)支持差 |
| 支持事务,可做增量采集 | 网络延迟高时效率低 |
JDBC采集适合OLTP类数据库,比如业务系统的MySQL、Oracle。对于OLAP或大数据组件,别死磕JDBC,后面几种技术更合适。
4.2 日志解析:采得深,但费功夫
日志解析,是从数据库的日志文件里提取元数据变更信息。比如MySQL的binlog、Oracle的redo log、PostgreSQL的WAL。为什么要搞这么复杂?因为有些场景下,你不仅要知道表结构长什么样,还要知道它什么时候变的、怎么变的。
我举个例子。你有一个核心业务表,每天被几十个任务读写。突然有一天,某个字段的类型从 int 改成了 varchar,导致下游ETL全挂了。如果你只做全量采集,可能第二天才发现。但如果你解析了binlog,变更发生的那一刻就能捕获到。
日志解析的典型流程:
- 日志订阅:连接到数据库的日志流,比如MySQL的binlog dump协议
- 事件解析:解析日志事件,识别DDL(ALTER、CREATE、DROP)和DML(INSERT、UPDATE、DELETE)
- 元数据提取:从DDL语句中提取变更后的表结构
- 版本管理:记录每次变更的时间戳和版本号
我用Canal做过MySQL的实时元数据采集,核心代码大概这样:
// Canal连接配置
CanalConnector connector = CanalConnectors.newSingleConnector(
new InetSocketAddress("127.0.0.1", 11111),
"destination", "username", "password");
connector.connect();
connector.subscribe(".*\\..*");
while (running) {
Message message = connector.getWithoutAck(100);
long batchId = message.getId();
for (CanalEntry.Entry entry : message.getEntries()) {
if (entry.getEntryType() == CanalEntry.EntryType.ROWDATA) {
CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(
entry.getStoreValue());
// 识别DDL事件
if (rowChange.getIsDdl()) {
String ddlSql = rowChange.getSql();
String schemaName = entry.getHeader().getSchemaName();
String tableName = entry.getHeader().getTableName();
// 解析DDL,提取新表结构
processDDL(schemaName, tableName, ddlSql);
}
}
}
connector.ack(batchId);
}
日志解析对运维要求很高。binlog的格式(ROW/STATEMENT/MIXED)会影响解析结果,日志的保留时长、磁盘空间、主从切换都会导致采集中断。我建议在生产环境先做压力测试,别一上来就全量订阅。
4.3 API对接:最优雅,但得看对方脸色
API对接,是通过数据源提供的RESTful或RPC接口来获取元数据。现在很多云服务和SaaS平台都提供了元数据API,比如AWS Glue、阿里云DataWorks、Snowflake的INFORMATION_SCHEMA。
为什么说它优雅?因为API是官方支持的,不会像JDBC那样有性能风险,也不会像日志解析那样依赖底层实现。你只需要调接口、解析JSON,就能拿到结构化的元数据。
但问题也很明显——每个平台的API都不一样。你对接了Snowflake,换到BigQuery就得重写一套。我整理过一份主流平台的元数据API对比:
| 平台 | API类型 | 采集内容 | 限流策略 |
|---|---|---|---|
| Snowflake | SQL + REST | 表、视图、字段、标签、策略 | 按仓库大小限流 |
| AWS Glue | REST (GetTables, GetPartitions) | 表、分区、存储格式、位置 | 每秒1000次 |
| Databricks | REST + Unity Catalog | 表、列、权限、血缘 | 按工作区限流 |
| Tableau | REST (Metadata API) | 数据源、工作表、字段、计算 | 每小时5000次 |
写一个简单的API调用示例(Python):
import requests
import json
def fetch_snowflake_metadata(account, , database):
url = f"https://{account}.snowflakecomputing.com/api/v2/databases/{database}/schemas"
headers = {
"Authorization": f"Bearer {}",
"Content-Type": "application/json"
}
# 获取所有schema
response = requests.get(url, headers=headers)
schemas = response.json()
metadata = []
for schema in schemas:
# 获取每个schema下的表
tables_url = f"{url}/{schema['name']}/tables"
tables_resp = requests.get(tables_url, headers=headers)
tables = tables_resp.json()
for table in tables:
metadata.append({
"database": database,
"schema": schema['name'],
"table": table['name'],
"columns": table['columns'],
"row_count": table.get('row_count', 0)
})
return metadata
API对接一定要处理好限流和重试。我习惯用指数退避策略,第一次失败等1秒,第二次等2秒,第三次等4秒,最多重试5次。另外,API返回的元数据往往是缓存快照,不是实时的。如果你需要秒级感知变更,API可能不够用。
4.4 爬虫技术:最后的武器
爬虫技术,听起来有点「野路子」,但在某些场景下是唯一的选择。比如:
- 老旧的ERP系统,没有API,也不开放数据库直连
- Web版的数据管理工具,比如Apache Atlas的Web UI
- 内部Wiki或文档系统,里面记录了数据字典
说白了,就是模拟人操作浏览器,从页面上抓取元数据。我一般用Selenium或Playwright来做。
给你看一个用Playwright爬取Atlas元数据的例子:
from playwright.sync_api import sync_playwright
def crawl_atlas_metadata(atlas_url, username, password):
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
# 登录
page.goto(f"{atlas_url}/login")
page.fill("#username", username)
page.fill("#password", password)
page.click("#login-btn")
page.wait_for_load_state("networkidle")
# 进入数据资产页面
page.goto(f"{atlas_url}/search?type=Table")
page.wait_for_selector(".table-row")
# 提取表列表
tables = []
rows = page.query_selector_all(".table-row")
for row in rows:
table_name = row.query_selector(".name").inner_text()
table_type = row.query_selector(".type").inner_text()
# 点击进入详情页
row.click()
page.wait_for_selector(".column-list")
columns = []
col_elements = page.query_selector_all(".column-item")
for col in col_elements:
columns.append({
"name": col.query_selector(".col-name").inner_text(),
"type": col.query_selector(".col-type").inner_text(),
"comment": col.query_selector(".col-comment").inner_text()
})
tables.append({
"name": table_name,
"type": table_type,
"columns": columns
})
# 返回列表页
page.go_back()
page.wait_for_selector(".table-row")
browser.close()
return tables
爬虫最大的问题是脆弱性。页面结构一变,爬虫就废了。有一次我爬一个内部系统,对方前端框架升级,所有CSS选择器都变了,我花了三天重新适配。后来我改用XPath加正则匹配,稍微抗造一点。但说实话,爬虫只适合做一次性或低频的采集,别把它当主力方案。
4.5 四种技术怎么选?一张图说清楚
下面这张图,是我自己总结的选型框架。你根据数据源的类型、变更频率、性能要求,对号入座就行。
4.6 组合拳才是王道
你可能会问:「那我到底用哪种?」我的答案是——别只押一种。
我做过一个金融客户的项目,他们的数据源包括:
- 核心交易库(Oracle):用JDBC做全量采集,每天凌晨跑一次
- 实时风控库(MySQL):用binlog日志解析做实时采集
- 数仓(Snowflake):用API对接,按需触发
- 内部数据字典(Confluence):用爬虫每周抓一次
你看,四种技术全用上了。每种技术解决一类问题,组合起来才能覆盖所有场景。
- 能调API就别爬虫——API稳定、高效、官方支持
- 需要实时就用日志——JDBC全量采集做不到秒级感知
- 老旧系统别强求——爬虫虽然丑,但能干活
- 性能优先选JDBC——简单直接,对开发者友好
嗯,这一章的内容就到这儿。四种技术各有千秋,没有的好坏。关键是你得清楚自己的数据源长什么样、变更频率多高、能接受多大的延迟。想清楚这些,选型就不难了。