3. 增量迁移工作流:Liquibase执行流程、如何判断哪些changeset已执行、如何保证幂等性

增量迁移是 Liquibase 在生产环境中最核心的使用模式。理解其执行流程、已执行 changeset 的判断机制以及幂等性保证原理,是进行性能调优和故障排查的基础。本章将深入剖析这三个关键环节。

3.1 Liquibase 增量迁移执行流程

Liquibase 的增量迁移并非简单顺序执行所有 changeset,而是遵循一套严谨的“检查-记录-执行”工作流。下图展示了从命令行启动到迁移完成的完整路径。

Liquibase 增量迁移执行流程 1. 启动迁移命令 2. 解析 changelog 文件 3. 查询 DATABASECHANGELOG 4. 比对未执行 changeset 5. 执行未应用 changeset 6. 写入 DATABASECHANGELOG 迁移完成 关键说明 步骤 3 和 4 是判断已执行 changeset 的核心环节 步骤 5 和 6 保证幂等性

上述流程中,步骤 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 执行以下判断:

  1. 键匹配检查:如果 (FILENAME, ID, AUTHOR) 三元组在已执行集合中存在,则初步认为该 changeset 已执行。
  2. MD5 校验检查:如果键匹配,进一步比对当前 changeset 的 MD5 值与数据库中的 MD5SUM 字段。
    • 一致:正常跳过,视为已执行。
    • 不一致:抛出 ValidationFailedException,提示 changeset 被非法修改,迁移中断。
  3. 键不匹配:该 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 如何防止重复执行:

幂等性保证:两次执行同一 changelog 第一次执行 changelog 执行 changeset A、B、C 记录 A、B、C 到跟踪表 第二次执行同一 changelog 查询跟踪表,发现 A、B、C 已存在 跳过 A、B、C,无任何变更 幂等性核心 第二次执行时,由于跟踪表中已存在记录,Liquibase 直接跳过所有 changeset。 数据库状态与第一次执行后完全一致,不会产生重复变更。

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。