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 幂等性执行流程
- Liquibase 解析变更日志,获取所有变更集列表。
- 对于每个变更集,查询
DATABASECHANGELOG表中是否存在FILENAME + ID + AUTHOR匹配的记录。 - 如果记录不存在: 执行该变更集,并将执行记录插入追踪表。
- 如果记录存在: 比较 MD5 校验和。若一致,则跳过执行;若不一致,则抛出异常(默认行为)或按配置处理。
- 所有变更集执行完毕后,更新
DATABASECHANGELOGLOCK表释放锁。
工程实践: 在 CI/CD 流水线中,每次部署前应执行
liquibase status --verbose 命令,查看哪些变更集将被执行。这有助于提前发现 MD5 校验和不匹配等问题,避免部署中断。
2.5 三者的协作关系
下面的 SVG 图展示了变更日志、变更集与 DATABASECHANGELOG 表在 Liquibase 执行过程中的协作流程。
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 变更集管理,否则将导致环境状态与代码版本不一致,引发灾难性后果。