2. Liquibase 核心概念:ChangeLog、ChangeSet、ID、Author、FileEncoding、Context、Label

Liquibase 的迁移体系建立在几个核心概念之上。理解这些概念是编写可维护、可追溯的数据库变更脚本的基础。本章将逐一拆解 ChangeLog、ChangeSet、ID、Author、FileEncoding、Context 和 Label 的定义、作用及最佳实践。

2.1 ChangeLog:迁移的根清单

ChangeLog 是 Liquibase 的入口文件,通常是一个 XML、YAML、JSON 或 SQL 格式的文件。它定义了数据库变更的版本清单,包含一个或多个 ChangeSet。Liquibase 通过解析 ChangeLog 来按顺序执行变更。

关键特性:
  • 格式灵活:支持 XML(默认)、YAML、JSON、SQL 四种格式。
  • 可嵌套:通过 <include> 或 <includeAll> 标签引入子 ChangeLog,实现模块化管理。
  • 版本控制:ChangeLog 文件本身应纳入 Git 等版本控制系统,作为数据库变更的“源代码”。
<!-- 示例:主 ChangeLog 文件 (db.changelog-master.xml) -->
<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.20.xsd">

    <include file="changelogs/001-create-tables.xml" relativeToChangelogFile="true"/>
    <include file="changelogs/002-add-indexes.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

2.2 ChangeSet:原子变更单元

ChangeSet 是 ChangeLog 中的最小执行单元,代表一次不可分割的数据库变更。每个 ChangeSet 包含一个或多个变更操作(如 createTable、addColumn、sql)。Liquibase 通过 ID + Author + FilePath 三元组来唯一标识一个 ChangeSet,并记录其是否已被执行。

最佳实践:每个 ChangeSet 应只包含一个逻辑变更(如只创建一张表,或只添加一个索引),避免将多个无关操作放在同一个 ChangeSet 中。这有助于回滚和问题排查。
<changeSet id="1" author="zhangsan">
    <createTable tableName="user">
        <column name="id" type="bigint" autoIncrement="true">
            <constraints primaryKey="true" nullable="false"/>
        </column>
        <column name="username" type="varchar(50)">
            <constraints unique="true" nullable="false"/>
        </column>
        <column name="email" type="varchar(100)"/>
    </createTable>
</changeSet>

2.3 ID 与 Author:唯一标识的基石

ID 和 Author 是 ChangeSet 的必填属性,与 ChangeLog 文件路径共同构成全局唯一标识。Liquibase 使用该标识在 DATABASECHANGELOG 表中记录执行状态,确保同一变更不会重复执行。

属性 说明 示例 注意事项
ID ChangeSet 的序号或标识符,在同一 ChangeLog 文件中应唯一 1, 001, 20250320-001 建议使用数字或日期前缀,便于排序和追溯
Author 编写该 ChangeSet 的作者名称 zhangsan, dev-team 建议使用统一的团队别名或系统用户名,避免使用个人昵称
常见错误:修改已执行 ChangeSet 的 ID 或 Author 会导致 Liquibase 认为这是一个新的 ChangeSet,从而重复执行。已发布的 ChangeSet 应视为不可变,只允许追加新的 ChangeSet。

2.4 FileEncoding:字符集一致性

FileEncoding 属性用于指定 ChangeLog 文件的字符编码。默认情况下,Liquibase 使用系统默认编码(通常为 UTF-8)。当 ChangeLog 中包含非 ASCII 字符(如中文注释、特殊符号)时,显式声明编码可避免乱码问题。

<databaseChangeLog
    xmlns="..."
    fileEncoding="UTF-8">
    ...
</databaseChangeLog>
推荐:始终将 fileEncoding 设置为 UTF-8,并在团队中统一使用 UTF-8 编码保存所有 ChangeLog 文件。

2.5 Context:环境隔离的开关

Context 允许为 ChangeSet 指定运行环境标签。Liquibase 在执行时通过 --contexts 参数过滤,只有匹配上下文的 ChangeSet 才会被执行。这解决了不同环境(开发、测试、生产)需要不同变更的问题。

<changeSet id="2" author="lisi" context="dev,test">
    <insert tableName="config">
        <column name="key" value="debug_mode"/>
        <column name="value" value="true"/>
    </insert>
</changeSet>

<changeSet id="3" author="lisi" context="prod">
    <insert tableName="config">
        <column name="key" value="debug_mode"/>
        <column name="value" value="false"/>
    </insert>
</changeSet>

执行命令示例:

# 仅执行 dev 和 test 环境的 ChangeSet
liquibase --contexts=dev,test update

# 仅执行 prod 环境的 ChangeSet
liquibase --contexts=prod update

2.6 Label:细粒度执行控制

Label 与 Context 类似,但更灵活。Label 可以附加到 ChangeSet 上,通过 --labels 参数进行包含或排除过滤。与 Context 的“环境”语义不同,Label 通常用于描述变更的“类型”或“特性”,如 schema-change、data-migration、hotfix。

特性 Context Label
语义 环境(dev, test, prod) 类型或特性(schema, data, hotfix)
过滤方式 精确匹配(逗号分隔) 支持表达式(!hotfix 排除,schema AND data 组合)
典型用途 环境隔离 变更分类、灰度发布、紧急修复
<changeSet id="4" author="wangwu" labels="schema-change">
    <addColumn tableName="user">
        <column name="phone" type="varchar(20)"/>
    </addColumn>
</changeSet>

<changeSet id="5" author="zhaoliu" labels="hotfix,data-migration">
    <update tableName="user">
        <column name="status" value="active"/>
        <where>status IS NULL</where>
    </update>
</changeSet>

执行命令示例:

# 仅执行 schema-change 标签的变更
liquibase --labels=schema-change update

# 执行除 hotfix 之外的所有变更
liquibase --labels=!hotfix update

2.7 核心概念关系图

下图展示了 ChangeLog、ChangeSet 及其属性之间的层级关系与执行流程:

Liquibase 核心概念关系图 ChangeLog fileEncoding="UTF-8" include / includeAll databaseChangeLog 根 ChangeSet ID="1" Author="zhang" Context="dev" Labels="schema" 执行时:Liquibase 解析 ChangeLog → 读取 ChangeSet → 根据 Context/Labels 过滤 → 检查 DATABASECHANGELOG 表 → 执行未运行变更

2.8 核心概念对比总结

概念 层级 是否必填 作用域 典型值
ChangeLog 根文件 是 全局 db.changelog-master.xml
ChangeSet 变更单元 是 文件内 包含 createTable、addColumn 等
ID ChangeSet 属性 是 ChangeSet 级别 1, 001, 20250320-001
Author ChangeSet 属性 是 ChangeSet 级别 zhangsan, dev-team
FileEncoding ChangeLog 属性 否 文件级别 UTF-8
Context ChangeSet 属性 否 环境过滤 dev, test, prod
Label ChangeSet 属性 否 类型过滤 schema-change, hotfix
核心原则:ID + Author + ChangeLog 文件路径构成 ChangeSet 的全局唯一标识。一旦 ChangeSet 被执行并记录到 DATABASECHANGELOG 表,其内容、ID 和 Author 均不应修改。如需调整,应创建新的 ChangeSet。