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 字段存血缘关系,结果查询时要用 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": "重试间隔采用指数退避策略"
  }
}
关键点:JSON 适合结构扁平、字段固定的元数据。如果元数据有复杂的继承关系或多态,JSON 的表达能力会捉襟见肘。

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>
注意:XML 解析时要注意 XXE(XML External Entity)攻击。我曾经在安全审计时发现一个遗留系统没禁用外部实体,差点酿成大祸。解析 XML 时务必关闭 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"
避坑指南:YAML 里字符串尽量用引号括起来。我曾经被 NO 自动转成布尔值 false 坑过,排查了半天才发现是 YAML 解析器自作聪明。

3.3 三种方案的选型决策树

说了这么多,到底怎么选?我画了一张决策图,帮你快速定位:

元数据存储与序列化选型决策树 元数据存储方案 需要持久化存储? 仅配置文件/交换? 关系型数据库 序列化格式选择 MySQL / PostgreSQL JSON(API交换) YAML(配置文件) 需要 Schema 校验? 需要注释/复杂结构? 需要强类型校验? JSON Schema YAML + 注释 XML + XSD 提示:实际项目中往往是混合方案——RDBMS 做持久化存储,JSON/YAML 做数据交换

3.4 混合存储策略:取长补短

实际项目中,我很少只用一种方案。更常见的做法是分层存储:

  1. 持久层:RDBMS 存储元数据的权威版本,保证一致性和事务
  2. 交换层:JSON 或 YAML 导出,用于系统间同步或备份
  3. 缓存层:Redis 或内存缓存,加速高频查询

举个例子,我之前做的数据血缘系统:

  • MySQL 存血缘关系的权威数据(表、字段、依赖关系)
  • 每天凌晨导出为 JSON 文件,供下游数据质量系统消费
  • YAML 配置文件管理数据源的连接信息,方便运维修改
核心原则:元数据存储没有标准答案。我的建议是——先搞清楚你的元数据是给谁用的、怎么用的、多大量级,然后再选方案。别为了技术而技术,简单够用才是王道。