4. ChangeLog 文件格式详解

Liquibase 支持四种 ChangeLog 文件格式:XML、YAML、JSON 和 SQL。每种格式都有其独特的语法结构、适用场景和性能特征。在并发迁移和高 GC 压力环境下,格式选择直接影响解析效率、内存占用和回滚能力。本章逐一剖析四种格式的核心语法、最佳实践和性能考量。

4.1 XML 格式详解

XML 是 Liquibase 的原生格式,也是功能最完整、支持最成熟的格式。它使用标签结构描述数据库变更,支持所有 Liquibase 特性,包括前置条件、上下文、标签和回滚指令。

核心特征: 完整的 XSD 校验、IDE 自动补全、最丰富的 Change Type 支持、原生回滚块。

4.1.1 基本结构

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
        http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.23.xsd">

    <changeSet id="1" author="jdoe">
        <preConditions onFail="MARK_RAN">
            <not>
                <tableExists tableName="employee"/>
            </not>
        </preConditions>
        <createTable tableName="employee">
            <column name="id" type="bigint" autoIncrement="true">
                <constraints primaryKey="true" nullable="false"/>
            </column>
            <column name="name" type="varchar(100)">
                <constraints nullable="false"/>
            </column>
            <column name="email" type="varchar(255)"/>
        </createTable>
        <rollback>
            <dropTable tableName="employee"/>
        </rollback>
    </changeSet>
</databaseChangeLog>

4.1.2 回滚块语法

回滚方式 语法示例 适用场景
内联回滚 <rollback><dropTable/></rollback> 简单 DDL 回滚,明确知道逆向操作
引用回滚 <rollback changeSetId="1" changeSetAuthor="jdoe"/> 复用已有 changeset 的回滚逻辑
空回滚 <rollback></rollback> 标记不可回滚的变更,执行时跳过
SQL 回滚 <rollback>DROP TABLE employee;</rollback> 复杂 DDL 或 DML 回滚,需要精确 SQL

4.1.3 性能考量

  • 解析开销: XML 解析器(SAX/StAX)在 JVM 上经过高度优化,但 DOM 解析会一次性加载整个文档到内存,大文件(>10MB)可能触发 Young GC 频繁。
  • 内存占用: 每个 changeset 的标签树在解析后形成 DOM 对象,1000+ changeset 的 XML 文件约占用 50-80MB 堆内存。
  • GC 优化建议: 使用 splitStatements 属性拆分大 SQL 块,避免单个节点包含超长文本导致字符串分配压力。
XML ChangeLog 解析与 GC 压力模型 XML 文件 StAX 解析器 DOM 对象树 GC 压力来源 字符串常量池膨胀 | 标签节点对象分配 | 大文本 char[] 分配 | XSD 校验临时对象 优化策略 拆分大文件 → 多文件 include 优化策略 使用 splitStatements 控制文本节点大小

4.2 YAML 格式详解

YAML 格式以缩进和键值对结构著称,比 XML 更简洁,可读性更高。在微服务和容器化部署中,YAML 是首选格式,因为它与 Kubernetes、Helm 等云原生工具风格一致。

推荐场景: 微服务项目、CI/CD 流水线、团队偏好 YAML 配置风格、需要频繁人工审查 ChangeLog 的场景。

4.2.1 基本结构

databaseChangeLog:
  - changeSet:
      id: 1
      author: jdoe
      preConditions:
        - onFail: MARK_RAN
        - not:
            tableExists:
              tableName: employee
      changes:
        - createTable:
            tableName: employee
            columns:
              - column:
                  name: id
                  type: bigint
                  autoIncrement: true
                  constraints:
                    primaryKey: true
                    nullable: false
              - column:
                  name: name
                  type: varchar(100)
                  constraints:
                    nullable: false
              - column:
                  name: email
                  type: varchar(255)
      rollback:
        - dropTable:
            tableName: employee

4.2.2 格式对比:XML vs YAML

维度 XML YAML
文件体积 较大(标签冗余) 较小(缩进结构)
解析速度 快(原生 StAX) 中等(SnakeYAML 解析)
内存占用 中等 较低(无标签节点)
IDE 支持 XSD 校验 + 自动补全 Schema 校验有限
回滚语法 内联标签或引用 内联列表或引用
学习曲线 中等 低(缩进直观)

4.2.3 回滚语法

# 内联回滚
rollback:
  - dropTable:
      tableName: employee

# 引用回滚
rollback:
  changeSetId: 1
  changeSetAuthor: jdoe

# 空回滚(标记不可回滚)
rollback:
YAML 缩进陷阱: YAML 使用空格缩进(禁止 Tab),缩进层级错误会导致解析失败。在 CI/CD 环境中,建议使用 yamllint 工具预校验 ChangeLog 文件,避免流水线中断。

4.3 JSON 格式详解

JSON 格式是四种格式中最轻量的,适合程序化生成和 API 驱动的数据库变更管理。它的结构直接映射为 Java 对象,解析路径最短,GC 压力最小。

4.3.1 基本结构

{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "1",
        "author": "jdoe",
        "preConditions": [
          {
            "onFail": "MARK_RAN",
            "not": {
              "tableExists": {
                "tableName": "employee"
              }
            }
          }
        ],
        "changes": [
          {
            "createTable": {
              "tableName": "employee",
              "columns": [
                {
                  "column": {
                    "name": "id",
                    "type": "bigint",
                    "autoIncrement": true,
                    "constraints": {
                      "primaryKey": true,
                      "nullable": false
                    }
                  }
                },
                {
                  "column": {
                    "name": "name",
                    "type": "varchar(100)",
                    "constraints": {
                      "nullable": false
                    }
                  }
                },
                {
                  "column": {
                    "name": "email",
                    "type": "varchar(255)"
                  }
                }
              ]
            }
          }
        ],
        "rollback": [
          {
            "dropTable": {
              "tableName": "employee"
            }
          }
        ]
      }
    }
  ]
}

4.3.2 性能优势

  • 解析速度: Jackson 或 Gson 解析 JSON 比 SnakeYAML 快 30-50%,比 DOM 解析 XML 快 2-3 倍。
  • 内存效率: JSON 节点直接映射为 Java Map/List,无标签对象开销,1000 changeset 约占用 30-40MB 堆内存。
  • GC 友好: 字符串数量少(无标签名),char[] 分配减少,Young GC 频率降低。

4.3.3 回滚语法

// 内联回滚
"rollback": [
  { "dropTable": { "tableName": "employee" } }
]

// 引用回滚
"rollback": {
  "changeSetId": "1",
  "changeSetAuthor": "jdoe"
}

// 空回滚
"rollback": []

4.4 SQL 格式详解

SQL 格式是唯一直接使用原生 SQL 语句的 ChangeLog 格式。它没有中间抽象层,DBA 可以直接编写和审查 SQL,适合复杂数据库操作和性能敏感场景。

核心特征: 零抽象层、DBA 友好、支持数据库专有语法、回滚需手动编写逆向 SQL。

4.4.1 基本结构

-- liquibase formatted sql
-- changeset jdoe:1
-- preconditions onFail:MARK_RAN
-- precondition not tableExists:employee
CREATE TABLE employee (
    id BIGINT AUTO_INCREMENT PRIMARY KEY NOT NULL,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(255)
);
-- rollback DROP TABLE employee;

-- changeset jdoe:2
-- preconditions onFail:MARK_RAN
-- precondition tableExists:employee
ALTER TABLE employee ADD COLUMN department VARCHAR(50);
-- rollback ALTER TABLE employee DROP COLUMN department;

4.4.2 格式对比:四种格式一览

维度 XML YAML JSON SQL
抽象层级 高 高 高 无
数据库无关性 是 是 是 否
回滚自动生成 支持 支持 支持 手动编写
解析速度 快 中等 最快 最快(纯文本)
GC 压力 中等 低 最低 最低
DBA 审查难度 高 中 中 低
IDE 支持 优秀 良好 良好 标准 SQL 支持

4.4.3 SQL 格式回滚最佳实践

  • 每条 changeset 必须包含 rollback 注释: 缺少 rollback 的 changeset 在回滚时会被跳过,可能导致数据库状态不一致。
  • 复杂 DDL 回滚使用多行: 使用 -- rollback 多行注释块编写逆向 SQL。
  • DML 回滚使用事务: 在 changeset 内使用 BEGIN/COMMIT 包裹,确保回滚原子性。
-- changeset jdoe:3
-- preconditions onFail:MARK_RAN
-- precondition tableExists:employee
BEGIN;
UPDATE employee SET department = 'Engineering' WHERE department IS NULL;
COMMIT;
-- rollback BEGIN;
-- rollback UPDATE employee SET department = NULL WHERE department = 'Engineering';
-- rollback COMMIT;

4.5 格式选择决策指南

ChangeLog 格式选择决策树 选择 ChangeLog 格式 需要数据库无关性? 固定数据库,DBA 主导? 团队偏好 XML 云原生/微服务 SQL 格式(原生 SQL) XML 格式 YAML 或 JSON (GC 敏感选 JSON) GC 压力排序:SQL < JSON < YAML < XML(高 GC 环境优先选择 JSON 或 SQL 格式)

4.6 格式迁移与混合使用

Liquibase 允许在同一个项目中混合使用多种格式,通过 include 或 includeAll 引用不同格式的 ChangeLog 文件。这种灵活性在大型项目迁移时非常有用。

<!-- 主 ChangeLog 混合引用多种格式 -->
<databaseChangeLog ...>
    <include file="classpath:changelogs/001-schema.xml"/>
    <include file="classpath:changelogs/002-data.yaml"/>
    <include file="classpath:changelogs/003-indexes.json"/>
    <include file="classpath:changelogs/004-migration.sql"/>
</databaseChangeLog>
混合使用注意事项:
  • 所有格式的 changeset ID 必须在全局唯一,跨格式重复 ID 会导致执行冲突。
  • SQL 格式的 changeset 无法自动生成回滚语句,混合项目中需确保 SQL 格式的 changeset 都包含手动 rollback。
  • 不同格式的解析器会分别加载,混合使用会增加类加载和解析器初始化的开销,建议不超过 2 种格式混合。

4.7 格式选择对 GC 停顿的影响总结

格式 解析器 对象分配速率 Young GC 频率 Full GC 风险 推荐场景
XML StAX/DOM 高 中 中(大文件) 传统企业、完整功能需求
YAML SnakeYAML 中 中低 低 微服务、云原生
JSON Jackson 低 低 极低 高并发、GC 敏感环境
SQL 纯文本 极低 极低 无 DBA 主导、固定数据库