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 块,避免单个节点包含超长文本导致字符串分配压力。
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 格式选择决策指南
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 主导、固定数据库 |