3. 增量迁移工作流:Liquibase执行流程、如何判断哪些changeset已执行、如何保证幂等性
增量迁移是 Liquibase 在生产环境中最核心的使用模式。理解其执行流程、已执行 changeset 的判断机制以及幂等性保证原理,是进行性能调优和故障排查的基础。本章将深入剖析这三个关键环节。
3.1 Liquibase 增量迁移执行流程
Liquibase 的增量迁移并非简单顺序执行所有 changeset,而是遵循一套严谨的“检查-记录-执行”工作流。下图展示了从命令行启动到迁移完成的完整路径。
上述流程中,步骤 3 和 4 是判断已执行 changeset 的核心,步骤 5 和 6 则共同保证了幂等性。下面分别深入分析。
3.2 如何判断哪些 changeset 已执行
Liquibase 通过维护一张名为 DATABASECHANGELOG 的跟踪表来记录所有已执行的 changeset。该表通常由 Liquibase 在首次运行时自动创建。
3.2.1 DATABASECHANGELOG 表结构
该表的核心字段用于唯一标识一个 changeset 及其执行状态:
| 字段名 | 类型 | 说明 |
|---|---|---|
ID |
VARCHAR | changeset 的 id 属性值 |
AUTHOR |
VARCHAR | changeset 的 author 属性值 |
FILENAME |
VARCHAR | changelog 文件的相对路径或逻辑路径 |
MD5SUM |
VARCHAR | changeset 内容的 MD5 校验和,用于检测变更 |
DATEEXECUTED |
TIMESTAMP | 执行完成的时间戳 |
ORDEREXECUTED |
INT | 全局执行顺序号 |
TAG |
VARCHAR | 可选的标签,用于回滚定位 |
LIQUIBASE |
VARCHAR | 执行该 changeset 的 Liquibase 版本 |
3.2.2 判断逻辑
Liquibase 在解析完所有 changelog 文件后,会执行以下 SQL 查询来获取已执行记录:
SELECT ID, AUTHOR, FILENAME, MD5SUM
FROM DATABASECHANGELOG
WHERE FILENAME IN ('path/to/changelog-1.xml', 'path/to/changelog-2.xml', ...)
ORDER BY ORDEREXECUTED ASC;
然后,Liquibase 在内存中构建一个“已执行集合”,其唯一键为 (FILENAME, ID, AUTHOR)。对于 changelog 中的每个 changeset,Liquibase 执行以下判断:
- 键匹配检查:如果
(FILENAME, ID, AUTHOR)三元组在已执行集合中存在,则初步认为该 changeset 已执行。 - MD5 校验检查:如果键匹配,进一步比对当前 changeset 的 MD5 值与数据库中的
MD5SUM字段。- 一致:正常跳过,视为已执行。
- 不一致:抛出
ValidationFailedException,提示 changeset 被非法修改,迁移中断。
- 键不匹配:该 changeset 未被记录,视为未执行,进入执行阶段。
最佳实践: 永远不要手动修改
DATABASECHANGELOG 表或已执行 changeset 的内容。如果确实需要重新执行某个 changeset,应使用 rollback 或 changelogSync 等官方命令。
3.3 如何保证幂等性
幂等性(Idempotency)是指无论执行多少次,结果都与执行一次相同。Liquibase 通过“执行前检查 + 执行后记录”的机制来保证增量迁移的幂等性。
3.3.1 幂等性保证的四个层次
| 层次 | 机制 | 说明 |
|---|---|---|
| 1. 全局幂等 | DATABASECHANGELOG 跟踪表 | 通过唯一键 (FILENAME, ID, AUTHOR) 确保每个 changeset 只执行一次。 |
| 2. 变更级幂等 | SQL 语句的幂等性 | 推荐使用 CREATE TABLE IF NOT EXISTS、ALTER TABLE ... IF EXISTS(特定数据库)等幂等 SQL。 |
| 3. 上下文幂等 | preConditions / runOnChange | 通过 <preConditions> 检查数据库状态,或使用 runOnChange="true" 在 changeset 变更时重新执行。 |
| 4. 回滚幂等 | rollback 标签 | 为每个 changeset 定义 <rollback> 脚本,确保回滚操作也能安全重复执行。 |
3.3.2 幂等性执行流程详解
以下是一个典型的幂等性执行场景,展示了 Liquibase 如何防止重复执行:
3.3.3 幂等性失效场景与对策
注意: 以下场景可能导致幂等性失效,需要特别防范。
- 手动删除跟踪表记录:删除
DATABASECHANGELOG中的某条记录后,对应的 changeset 会被重新执行,可能导致重复创建对象或数据重复插入。 - 修改已执行 changeset 的 id 或 author:这会改变唯一键,Liquibase 会将其视为新的 changeset 并执行,导致重复变更。
- 使用
runAlways="true":该属性会强制每次迁移都执行该 changeset,即使跟踪表中已有记录。应谨慎使用,通常仅用于数据修复或临时脚本。 - 多线程并发执行:多个 Liquibase 实例同时执行同一 changelog 时,可能因竞态条件导致重复执行。解决方案是使用数据库锁(如
SELECT ... FOR UPDATE)或确保同一时间只有一个实例执行迁移。
3.4 实战:查看跟踪表状态
以下 SQL 可用于直接查询 DATABASECHANGELOG 表,验证已执行 changeset 的状态:
-- 查看所有已执行的 changeset
SELECT ID, AUTHOR, FILENAME, DATEEXECUTED, MD5SUM, TAG
FROM DATABASECHANGELOG
ORDER BY ORDEREXECUTED ASC;
-- 检查特定 changeset 是否已执行
SELECT COUNT(*) AS EXECUTED_COUNT
FROM DATABASECHANGELOG
WHERE FILENAME = 'db/changelog/v1.0/001-create-user-table.xml'
AND ID = '1'
AND AUTHOR = 'dev-team';
-- 查看最近执行的 5 个 changeset
SELECT ID, AUTHOR, FILENAME, DATEEXECUTED
FROM DATABASECHANGELOG
ORDER BY DATEEXECUTED DESC
LIMIT 5;
排错要点: 当遇到“changeset 未执行但数据库对象已存在”的异常时,首先检查
DATABASECHANGELOG 表中是否缺少对应记录。如果记录缺失,可以使用 liquibase changelogSync 命令手动标记该 changeset 为已执行,而无需实际执行 SQL。