3、元数据存储与序列化:关系型数据库存储、JSON/XML/YAML序列化格式对比
元数据存哪儿?怎么存?这问题看着简单,但选错了后面全是坑。
我见过不少团队,一开始图省事全塞 JSON 文件里,结果数据量一上来,查个东西得遍历整个目录。也见过反过来的——明明就几十条配置,非得上 MySQL,搞得部署还得搭个数据库。
说白了,存储和序列化没有银弹。你得根据元数据的规模、使用方式和交换场景来选。这一节,我把三种主流方案掰开揉碎讲清楚。
3.1 关系型数据库存储:结构化元数据的基石
关系型数据库(RDBMS)是我个人最常用的元数据存储方案。为什么?因为它有 schema 约束、支持事务、查询能力强。
你想想看,元数据要是乱七八糟的,后面数据治理根本没法做。RDBMS 的表结构天然适合管理实体关系明确的元数据。
3.1.1 表结构设计原则
我在项目中遇到过最典型的场景:数据仓库的元数据管理。几百张表,每张表有字段、分区、生命周期、血缘关系。用 RDBMS 存,核心就三张表:
-- 表/视图元数据
CREATE TABLE metadata_tables (
table_id INT PRIMARY KEY AUTO_INCREMENT,
table_name VARCHAR(255) NOT NULL,
database_name VARCHAR(255) NOT NULL,
table_type ENUM('TABLE', 'VIEW', 'EXTERNAL') DEFAULT 'TABLE',
owner VARCHAR(128),
create_time DATETIME,
update_time DATETIME,
description TEXT,
UNIQUE KEY uk_table (database_name, table_name)
);
-- 字段元数据
CREATE TABLE metadata_columns (
column_id INT PRIMARY KEY AUTO_INCREMENT,
table_id INT NOT NULL,
column_name VARCHAR(255) NOT NULL,
data_type VARCHAR(64) NOT NULL,
is_nullable BOOLEAN DEFAULT TRUE,
is_primary_key BOOLEAN DEFAULT FALSE,
default_value VARCHAR(512),
column_comment TEXT,
ordinal_pos INT NOT NULL,
FOREIGN KEY (table_id) REFERENCES metadata_tables(table_id)
);
-- 分区信息
CREATE TABLE metadata_partitions (
partition_id INT PRIMARY KEY AUTO_INCREMENT,
table_id INT NOT NULL,
partition_col VARCHAR(255) NOT NULL,
partition_val VARCHAR(512) NOT NULL,
row_count BIGINT DEFAULT 0,
storage_size BIGINT DEFAULT 0,
FOREIGN KEY (table_id) REFERENCES metadata_tables(table_id)
);
update_time 和版本号。别问我为什么——有一次数据对不上,全靠时间戳回溯才找到问题根源。
3.1.2 查询与索引优化
元数据查询有个特点:读多写少。所以索引策略要偏向查询加速。
- 高频查询字段:表名、库名、字段名——建联合索引
- 模糊搜索:用
LIKE还是全文索引?数据量超过百万行,全文索引是必须的 - 血缘关系:用邻接表还是路径枚举?我建议小规模用邻接表,大规模用
materialized path
JSON_CONTAINS,性能惨不忍睹。后来拆成单独的血缘关系表,查询时间从秒级降到毫秒级。
3.2 JSON/XML/YAML 序列化格式对比
序列化格式的选择,说白了就是可读性、解析效率和表达能力之间的权衡。
我直接给结论:内部系统间交换用 JSON,配置文件用 YAML,遗留系统对接用 XML。下面这张表把差异说清楚:
| 特性 | JSON | XML | YAML |
|---|---|---|---|
| 可读性 | 中等 | 差(标签冗余) | 优秀(缩进结构) |
| 解析速度 | 快 | 慢(需 DOM/SAX) | 中等 |
| 数据体积 | 小 | 大(标签重复) | 中等 |
| Schema 支持 | JSON Schema | XSD(强类型) | 有限 |
| 注释支持 | 不支持 | 支持 | 支持 |
| 命名空间 | 不支持 | 原生支持 | 不支持 |
| 典型场景 | API 数据交换 | 文档/配置/SOAP | 配置文件/CI/CD |
3.2.1 JSON:现代数据交换的事实标准
JSON 为什么流行?轻量、原生 JavaScript 支持、生态丰富。几乎所有编程语言都有现成的解析库。
但 JSON 有个硬伤:不支持注释。你想想看,元数据定义里不加注释,别人怎么理解这个字段的含义?
我的做法是:在 JSON 里约定一个 _comment 字段,虽然不规范,但实用。
{
"_comment": "数据源连接配置,生产环境请使用加密存储",
"datasource": {
"type": "mysql",
"host": "192.168.1.100",
"port": 3306,
"database": "metadata_db",
"username": "meta_user",
"password": "encrypted_value_here"
},
"sync_config": {
"interval_sec": 3600,
"retry_times": 3,
"_comment": "重试间隔采用指数退避策略"
}
}
3.2.2 XML:老牌劲旅,但太重了
XML 我接触得早,2000 年代初做 EAI(企业应用集成)时,全是 XML 消息。它的优势是强类型、命名空间、XSLT 转换。
但说实话,现在新项目我基本不用 XML 了。原因很简单:解析成本高、手写太痛苦。
不过有一种场景 XML 依然不可替代:需要严格 schema 校验的元数据交换。比如金融行业的监管报送,XSD 定义得死死的,少一个字段都不行。
<?xml version="1.0" encoding="UTF-8"?>
<metadata:table xmlns:metadata="http://example.com/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://example.com/metadata metadata.xsd">
<metadata:name>user_profile</metadata:name>
<metadata:database>analytics</metadata:database>
<metadata:columns>
<metadata:column>
<metadata:name>user_id</metadata:name>
<metadata:type>BIGINT</metadata:type>
<metadata:primaryKey>true</metadata:primaryKey>
</metadata:column>
<metadata:column>
<metadata:name>user_name</metadata:name>
<metadata:type>VARCHAR(128)</metadata:type>
</metadata:column>
</metadata:columns>
</metadata:table>
external entity processing。
3.2.3 YAML:配置文件的王者
YAML 是我个人最喜欢的序列化格式。为什么?可读性太好了。缩进结构天然适合表达层级关系,而且支持注释和多行字符串。
但 YAML 也有坑:缩进敏感、类型推断容易出错。比如 yes 会被解析成布尔值 true,0123 会被解析成八进制数。
# 数据管道元数据配置
pipeline:
name: "user_etl_pipeline"
version: 2.1.0
source:
type: kafka
topic: "user_events"
bootstrap_servers:
- "kafka01:9092"
- "kafka02:9092"
transformations:
- name: "filter_invalid"
type: "sql_filter"
condition: "user_id IS NOT NULL"
- name: "enrich_with_dim"
type: "lookup_join"
dimension_table: "dim_user"
sink:
type: hdfs
path: "/data/warehouse/user_profile"
partition_by: "dt"
format: "parquet"
NO 自动转成布尔值 false 坑过,排查了半天才发现是 YAML 解析器自作聪明。
3.3 三种方案的选型决策树
说了这么多,到底怎么选?我画了一张决策图,帮你快速定位:
3.4 混合存储策略:取长补短
实际项目中,我很少只用一种方案。更常见的做法是分层存储:
- 持久层:RDBMS 存储元数据的权威版本,保证一致性和事务
- 交换层:JSON 或 YAML 导出,用于系统间同步或备份
- 缓存层:Redis 或内存缓存,加速高频查询
举个例子,我之前做的数据血缘系统:
- MySQL 存血缘关系的权威数据(表、字段、依赖关系)
- 每天凌晨导出为 JSON 文件,供下游数据质量系统消费
- YAML 配置文件管理数据源的连接信息,方便运维修改