2. Liquibase 核心概念:变更集、变更日志与数据库状态追踪表

在 Liquibase 的体系结构中,变更集(Changeset)、变更日志(Changelog) 和 数据库状态追踪表(DATABASECHANGELOG) 是构成数据库变更管理的三大基石。理解这三者的定义、关系及运作机制,是掌握 Liquibase 的关键。

2.1 核心概念总览

下表从定义、作用、存储位置三个维度对三个核心概念进行对比,帮助建立整体认知。

概念 定义 核心作用 存储位置
变更集(Changeset) 一个原子化的数据库变更单元,包含一条或多条 SQL/DCL 语句。 定义“做什么变更”,如建表、加索引、改字段。 变更日志文件内部
变更日志(Changelog) 一个有序的、包含多个变更集的清单文件(XML/YAML/JSON/SQL)。 定义“变更的顺序与组织方式”,是 Liquibase 的执行入口。 版本控制仓库(如 Git)
DATABASECHANGELOG Liquibase 自动在目标数据库中创建的状态追踪表。 记录“哪些变更集已经执行过”,防止重复执行。 目标数据库内部
关键认知: 变更日志是“剧本”,变更集是“每一幕的台词”,DATABASECHANGELOG 是“已演过的场次记录”。三者协同,确保数据库变更可追溯、可重复、可回滚。

2.2 变更集(Changeset)—— 原子变更单元

每个变更集必须包含两个唯一标识属性:id(开发者自定义)和 author(开发者名称)。Liquibase 使用 文件名 + id + author 三元组来全局唯一标识一个变更集。

2.2.1 变更集的基本结构(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">

    <changeSet id="create-user-table" author="zhangsan">
        <createTable tableName="app_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>

</databaseChangeLog>

2.2.2 变更集的常用属性

属性 说明 是否必填
id 变更集唯一标识,建议使用语义化名称(如 add-email-column) 是
author 变更集的创建者,通常使用 Git 用户名 是
runAlways 设为 true 时,每次运行都会执行该变更集(即使已执行过) 否
runOnChange 设为 true 时,仅当变更集内容发生变化时才重新执行 否
context 指定该变更集仅在特定上下文(如 dev、prod)中执行 否
failOnError 设为 false 时,该变更集执行失败不会中断整个流程 否
实践避坑: 不要滥用 runAlways 和 runOnChange。它们会绕过 DATABASECHANGELOG 的幂等性检查,可能导致生产环境数据被意外覆盖。仅在初始化数据、测试环境等受控场景中使用。

2.3 变更日志(Changelog)—— 变更的组织者

变更日志是 Liquibase 执行的入口文件。它通过 <include> 或 <includeAll> 标签将多个变更集文件组织成一个有序的执行序列。

2.3.1 主变更日志示例

<databaseChangeLog ...>
    <!-- 按顺序引入子变更日志 -->
    <include file="changelogs/001-create-schema.xml" relativeToChangelogFile="true"/>
    <include file="changelogs/002-create-user-table.xml" relativeToChangelogFile="true"/>
    <include file="changelogs/003-add-indexes.xml" relativeToChangelogFile="true"/>

    <!-- 引入整个目录下的所有变更日志 -->
    <includeAll path="changelogs/release-2.0/" relativeToChangelogFile="true"/>
</databaseChangeLog>

2.3.2 变更日志的组织原则

  • 按版本组织: 每个发布版本对应一个子目录,如 release-1.0/、release-2.0/。
  • 按功能模块拆分: 将用户模块、订单模块的变更集放在不同文件中。
  • 保持线性顺序: 变更日志中的 <include> 顺序决定了变更的执行顺序,不可随意调换。
  • 使用相对路径: 推荐设置 relativeToChangelogFile="true",使路径相对于变更日志文件本身,提高可移植性。

2.4 DATABASECHANGELOG —— 幂等性保障者

当 Liquibase 首次对目标数据库执行变更时,会自动创建两张追踪表:DATABASECHANGELOG 和 DATABASECHANGELOGLOCK。

2.4.1 DATABASECHANGELOG 表结构

字段名 类型 说明
ID VARCHAR(255) 变更集的 id 属性值
AUTHOR VARCHAR(255) 变更集的 author 属性值
FILENAME VARCHAR(255) 变更集所在的文件路径(相对于变更日志根目录)
DATEEXECUTED TIMESTAMP 变更集执行完成的时间戳
ORDEREXECUTED INT 执行顺序号,从 1 开始递增
MD5SUM VARCHAR(35) 变更集内容的 MD5 校验和,用于检测变更集是否被修改
DESCRIPTION VARCHAR(255) Liquibase 自动生成的变更描述(如 createTable tableName=app_user)
TAG VARCHAR(255) 可选的标签,用于标记回滚点
LIQUIBASE VARCHAR(20) 执行该变更集的 Liquibase 版本号

2.4.2 幂等性执行流程

  1. Liquibase 解析变更日志,获取所有变更集列表。
  2. 对于每个变更集,查询 DATABASECHANGELOG 表中是否存在 FILENAME + ID + AUTHOR 匹配的记录。
  3. 如果记录不存在: 执行该变更集,并将执行记录插入追踪表。
  4. 如果记录存在: 比较 MD5 校验和。若一致,则跳过执行;若不一致,则抛出异常(默认行为)或按配置处理。
  5. 所有变更集执行完毕后,更新 DATABASECHANGELOGLOCK 表释放锁。
工程实践: 在 CI/CD 流水线中,每次部署前应执行 liquibase status --verbose 命令,查看哪些变更集将被执行。这有助于提前发现 MD5 校验和不匹配等问题,避免部署中断。

2.5 三者的协作关系

下面的 SVG 图展示了变更日志、变更集与 DATABASECHANGELOG 表在 Liquibase 执行过程中的协作流程。

Liquibase 核心概念协作流程 变更日志 (Changelog) 变更集 #1 (id=001) 变更集 #2 (id=002) 变更集 #3 (id=003) 查询 DATABASECHANGELOG 记录存在 → 跳过执行 记录不存在 → 执行变更 插入执行记录到追踪表 DATABASECHANGELOG 表 确保幂等性,防止重复执行

2.6 实践要点与常见问题

2.6.1 变更集编写规范

  • 单一职责: 每个变更集只做一件事(如只建一张表或只加一个字段),便于回滚和排查。
  • 幂等性设计: 使用 preConditions 标签检查表或字段是否存在,避免重复执行时报错。
  • 避免修改已发布的变更集: 一旦变更集被合并到主分支并执行过,不应再修改其内容。如需调整,应创建新的变更集。

2.6.2 DATABASECHANGELOG 表维护

场景 推荐操作 风险说明
变更集内容需要微调 创建新的变更集,并在新变更集中执行 DROP/CREATE 或 ALTER 直接修改已执行变更集可能导致 MD5 校验失败,部署中断
需要手动删除某条执行记录 使用 DELETE FROM DATABASECHANGELOG WHERE ... 并谨慎操作 删除记录后,Liquibase 会重新执行该变更集,可能导致数据丢失或重复
需要回滚到某个标签 使用 liquibase rollback <tag> 命令 确保变更集中已配置 <rollback> 脚本,否则回滚可能失败
生产环境红线: 严禁手动修改 DATABASECHANGELOG 表或直接操作数据库来“绕过” Liquibase。所有数据库变更必须通过 Liquibase 变更集管理,否则将导致环境状态与代码版本不一致,引发灾难性后果。