Liquibase 使用教程
本文记录当前项目从 Flyway 脚本切换到 Liquibase 后的基本概念和实战操作。
1. 基本概念
1.1 changeSet
changeSet 是 Liquibase 的最小执行单元。Liquibase 会根据以下三项判断一个 changeSet 是否已经执行过:
FILENAMEIDAUTHOR
例如:
text
db.changelog-rule-migration.xml::rule-migration-08-rule-task-by-136-20260623::lix
只要这三项在 DATABASECHANGELOG 中存在,Liquibase 就认为这个 changeSet 已经执行过,后续 update 会跳过。
1.2 changelog
changelog 是 Liquibase 的入口配置文件,用来组织多个 changeSet 或 include 其他 changelog 文件。
当前项目常用入口:
| 文件 | 用途 |
|---|---|
db.changelog-master.xml |
全量初始化入口 |
db.changelog-incremental.xml |
增量更新入口 |
db.changelog-rule-migration.xml |
rule-migration 数据迁移入口 |
db.changelog-test.xml |
本地事务回滚验证入口 |
1.3 DATABASECHANGELOG
Liquibase 执行成功一个 changeSet 后,会在目标库的 DATABASECHANGELOG 表中记录执行历史。
常用查看语句:
sql
SELECT ID, AUTHOR, FILENAME, DATEEXECUTED, ORDEREXECUTED, MD5SUM
FROM DATABASECHANGELOG
ORDER BY ORDEREXECUTED;
1.4 DATABASECHANGELOGLOCK
Liquibase 执行时会通过 DATABASECHANGELOGLOCK 加锁,避免多个 Liquibase 同时更新同一个库。
如果异常中断后锁未释放,可以先确认锁状态:
sql
SELECT *
FROM DATABASECHANGELOGLOCK;
确认没有 Liquibase 进程在执行时,才可以手工释放:
sql
UPDATE DATABASECHANGELOGLOCK
SET LOCKED = 0,
LOCKEDBY = NULL,
LOCKGRANTED = NULL
WHERE ID = 1;
2. 当前项目目录约定
主要目录:
text
Db.Scripts/
changelog/
db.changelog-master.xml
db.changelog-incremental.xml
db.changelog-rule-migration.xml
db.changelog-test.xml
drop1/
drop2/
drop3/
rule-migration/
local/
sql/
init/
DDL/
DML/
约定:
- 原始 SQL 主要在
Db.Scripts/sql/init下。 - Liquibase XML 和迁移入口在
Db.Scripts/changelog下。 drop1~drop3按版本阶段组织。rule-migration用于独立执行迁移数据。local可放本地验证脚本,不应挂到正式入口中。
3. Docker 执行模板
建议本地验证时挂载 changelog 目录,这样修改脚本后不用重新打镜像。
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=<changelog-file> `
<command>
如果镜像内已经包含驱动,不需要额外挂载 driver。如果没有包含驱动,需要按实际镜像方案处理驱动依赖。
4. 常用命令
4.1 validate
只校验 changelog 和数据库记录是否一致,不执行业务 SQL。
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-master.xml `
validate
4.2 update-sql
预览即将执行的 SQL,不真正改库。
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-master.xml `
update-sql
4.3 update
真正执行未执行过的 changeSet。
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-master.xml `
update
建议执行顺序:
text
validate -> update-sql -> update
4.4 常用场景速查
下面命令都沿用第 3 节 Docker 模板,只替换最后的 <command>。实际执行时需要确认 --changelog-file 使用的是当前场景对应入口,例如:
- 全量初始化:
db.changelog-master.xml - 增量更新:
db.changelog-incremental.xml - 规则迁移:
db.changelog-rule-migration.xml
| 场景 | 推荐命令 | 说明 |
|---|---|---|
| 看还有哪些 changeSet 没执行 | status --verbose |
只查看待执行变更,不改库。适合执行 update 前确认范围。 |
| 看历史执行记录 | history |
查看 Liquibase 已执行过哪些 changeSet。适合排查某个版本是否已经跑过。 |
| 校验 changelog | validate |
只检查 Liquibase 文件、引用路径、属性等,不校验 SQL 业务语义。 |
| 预览即将执行 SQL | update-sql |
不改库,只输出 Liquibase 准备执行的 SQL。 |
| 正式执行增量 | update |
执行未记录在 DATABASECHANGELOG 中的 changeSet。 |
| checksum 报错后重新计算 | clear-checksums |
清空 DATABASECHANGELOG.MD5SUM,下次运行时重新计算;不会重跑已成功 changeSet。 |
| 查看 Liquibase 锁 | list-locks |
查看 DATABASECHANGELOGLOCK 是否被占用。 |
| 异常中断后释放锁 | release-locks |
确认没有 Liquibase 正在执行后再使用,避免并发更新同一个库。 |
| 旧库做基线 | changelog-sync |
不执行 SQL,只把未执行 changeSet 标记为已执行。 |
| 预览基线写入记录 | changelog-sync-sql |
不改库,预览 changelog-sync 会写入哪些记录。 |
| 执行到指定 tag | update-to-tag --tag=<tag-name> |
从当前状态执行到指定 tag 为止,适合验证 drop 级阶段。 |
| 预览执行到指定 tag | update-to-tag-sql --tag=<tag-name> |
不改库,只预览执行到指定 tag 前会执行哪些 SQL。 |
| 回滚前预览 SQL | rollback-count-sql <count> |
不改库,查看回滚最近 N 个 changeSet 的 SQL。 |
| 回滚最近 N 个 changeSet | rollback-count <count> |
真正执行回滚,依赖 changeSet 是否有 rollback 或 Liquibase 是否支持自动回滚。 |
status:查看待执行 changeSet
适用场景:
- 想确认本次
update会不会执行新脚本。 - 多人提交 changelog 后,想看目标库还差哪些 changeSet。
- 使用 context/label 时,确认过滤条件下有哪些 changeSet 会被执行。
命令:
powershell
docker run --rm `
-v "${PWD}\ Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-incremental.xml `
status --verbose
history:查看已执行历史
适用场景:
- 确认某个版本脚本是否已经执行成功。
- 排查
update为什么跳过某个 changeSet。 - 和
DATABASECHANGELOG表记录交叉核对。
命令:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-incremental.xml `
history
update-to-tag:执行到指定 tag
适用场景:
- 只想把测试库执行到某个 drop 阶段,例如
drop1。 - 验证某一阶段的初始化和回滚,不想继续执行后续 drop。
预览命令:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-master.xml `
update-to-tag-sql --tag=drop1
真正执行:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-master.xml `
update-to-tag --tag=drop1
执行后可以通过 DATABASECHANGELOG.TAG 确认是否到达目标 tag:
sql
SELECT ORDEREXECUTED, ID, AUTHOR, FILENAME, TAG, DATEEXECUTED
FROM DATABASECHANGELOG
ORDER BY ORDEREXECUTED;
list-locks / release-locks:处理锁
适用场景:
- Liquibase 执行异常中断。
- 再次执行时报
DATABASECHANGELOGLOCK已锁定。 - 需要确认锁是谁持有、何时加锁。
先查看锁:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-incremental.xml `
list-locks
确认没有 Liquibase 进程还在执行后,再释放锁:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-incremental.xml `
release-locks
注意:release-locks 只解决 Liquibase 锁记录,不会回滚已经执行过的 SQL。使用前要确认没有其他部署任务正在跑。
clear-checksums:处理 checksum 不一致
适用场景:
- 已执行的 changeSet 后续被格式化或做了无业务影响调整。
- Liquibase 报
Validation Failed,提示旧 checksum 和新 checksum 不一致。 - 确认不需要重跑该 changeSet,只希望重新计算 checksum。
命令:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
clear-checksums
注意:clear-checksums 不会让已执行成功的 changeSet 重新执行。如果要重跑某个 changeSet,需要先确认业务数据影响,再处理 DATABASECHANGELOG 对应记录。
changelog-sync / changelog-sync-sql:已有库做基线
适用场景:
- 旧库已经通过 Flyway 或人工 SQL 建好了对象。
- 不希望 Liquibase 重复执行历史脚本。
- 希望 Liquibase 从某个基线后开始接管后续增量。
先预览将写入的基线记录:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-master.xml `
changelog-sync-sql
确认无误后执行基线同步:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-master.xml `
changelog-sync
注意:changelog-sync 只写 Liquibase 执行记录,不会检查业务表是否真的存在,也不会补表或补数据。
5. 空库和已有库的区别
5.1 空库
空库没有业务表,也没有 Liquibase 记录表。执行 update 时,Liquibase 会:
- 创建
DATABASECHANGELOG。 - 创建
DATABASECHANGELOGLOCK。 - 按 changelog 顺序执行所有未执行 changeSet。
- 成功一个 changeSet,记录一条执行历史。
空库适合验证全量初始化。
5.2 已有库
已有库可能已经通过 Flyway 或人工 SQL 初始化过,但 Liquibase 不会自动读取 Flyway 历史。
如果已有库没有 DATABASECHANGELOG,直接执行 db.changelog-master.xml update,Liquibase 会认为全部 changeSet 都未执行,可能导致:
- 重复建表。
- 重复加字段。
- 重复插入初始化数据。
- 主键或唯一索引冲突。
已有库切换到 Liquibase 时,需要先做基线或使用增量入口。
6. Flyway 旧库切换 Liquibase
6.1 使用 changelog-sync 做基线
如果旧库中脚本已经通过 Flyway 执行过,并且当前数据库结构和数据已经与 changelog 对齐,可以使用 changelog-sync 标记基线。
作用:
- 不执行 changeSet 中的 SQL。
- 只往
DATABASECHANGELOG写入执行记录。 - 后续
update会跳过这些已同步的 changeSet。
命令:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-master.xml `
changelog-sync
注意:changelog-sync 只同步 Liquibase 记录,不校验业务表是否真的存在,也不补数据。
6.2 使用 db.changelog-incremental.xml 做增量
当前项目提供 db.changelog-incremental.xml 作为增量入口,只包含旧 Flyway 已执行脚本之后的增量部分。
适用场景:
- 现场库已经有旧版本业务表和初始化数据。
- 不希望 Liquibase 重跑旧 Flyway 脚本。
- 只希望执行后续新增脚本。
命令示例:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-incremental.xml `
update
7. rule-migration 数据迁移
db.changelog-rule-migration.xml 是独立入口,用来执行 rule-migration 下的数据迁移 SQL。
执行前建议:
- 先确认目标库已经完成基础表结构初始化。
- 先执行
validate。 - 再执行
update-sql。 - 最后执行
update。
命令:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-rule-migration.xml `
update
如果 01~07 已成功,08 失败,则修复 08 后重新执行同一个 update,Liquibase 会跳过 01~07,从未成功记录的 08 继续执行。
8. 重复数据和幂等处理
迁移数据时常见错误:
text
Duplicate entry '<id>' for key 'PRIMARY'
表示目标库中已经存在相同主键数据。
常见原因:
- 旧 Flyway 初始化数据已经插入过。
- rule-migration 导出的数据包含基础系统数据。
- 脚本重复执行。
处理策略:
| 策略 | 说明 |
|---|---|
保持 INSERT |
适合只执行一次、确保目标库无重复数据的脚本 |
DELETE + INSERT |
适合希望固定覆盖某些基础数据的场景 |
REPLACE INTO |
可处理重复主键,但本质是删除后插入,需注意外键、触发器、级联影响 |
INSERT IGNORE |
重复时跳过,适合已有数据优先的场景 |
| 新增补偿 changeSet | 已执行过的 changeSet 不建议直接改,推荐新增 changeSet 修正数据 |
已执行成功的 changeSet 不建议再修改内容,否则会出现 checksum 错误。
9. checksum 错误
报错示例:
text
Validation Failed:
changesets check sum
was: <old-md5>
but is now: <new-md5>
原因:
- changeSet 已经执行成功。
- 后来修改了对应 XML 或 sqlFile 内容。
- Liquibase 重新计算 checksum 后发现与
DATABASECHANGELOG.MD5SUM不一致。
推荐处理:
- 已执行成功的 changeSet 尽量恢复原内容。
- 如果需要补数据,新增 changeSet。
- 如果只是格式、幂等化调整,并确认数据库状态无需重跑,可以使用
clear-checksums。
命令:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
clear-checksums
注意:clear-checksums 不会重新执行已经成功的 changeSet,只是清空已记录的 checksum,让下次运行重新计算。
10. 从头重跑某个 changelog
Liquibase 是否执行 changeSet,取决于 DATABASECHANGELOG 记录。
如果希望同一个 changelog 从头重跑,可以删除对应记录:
sql
SELECT ID, AUTHOR, FILENAME, ORDEREXECUTED, DATEEXECUTED
FROM DATABASECHANGELOG
WHERE FILENAME = 'db.changelog-rule-migration.xml'
ORDER BY ORDEREXECUTED;
确认无误后:
sql
DELETE FROM DATABASECHANGELOG
WHERE FILENAME = 'db.changelog-rule-migration.xml';
然后重新执行 update。
注意:
- 只删除
DATABASECHANGELOG不会删除业务数据。 - 如果 SQL 不是幂等的,重跑仍然可能主键冲突。
- 测试库可以这样做,生产环境应谨慎。
也可以通过修改 changelog 文件名让 Liquibase 认为这是另一批 changeSet,但不推荐作为常规方案,因为它会制造新的执行历史,后续维护不清晰。
11. 事务和回滚
11.1 changeSet 内事务回滚
Liquibase 的 changeSet 默认:
xml
runInTransaction="true"
对于 MariaDB/MySQL 的 InnoDB 表,如果一个 changeSet 中有多条 DML,第 9 条失败,则前面已经成功执行的 DML 会跟随该 changeSet 事务回滚。
已验证场景:
pf_message表中同一个 changeSet 放 10 条INSERT。- 第 9 条故意使用第 1 条相同主键。
- 执行失败后查询测试 ID,前 8 条没有保留。
说明当前 DML 场景中,同一个 changeSet 内失败会回滚前面已执行的 DML。
边界:
- 只回滚失败的当前 changeSet。
- 不会回滚之前已经成功提交的其他 changeSet。
- DDL 在 MariaDB/MySQL 中可能隐式提交,不能完全依赖事务回滚。
- 如果设置
runInTransaction="false",则不保证同一 changeSet 内整体回滚。
11.2 Liquibase rollback
Liquibase 的 rollback 是部署回滚,不等同于数据库事务回滚。
常用预览:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-master.xml `
rollback-count-sql 1
真正回滚最近 1 个 changeSet:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-master.xml `
rollback-count 1
如果使用 tag:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-master.xml `
rollback --tag=<tag-name>
注意:回滚到某个 tag,表示回滚该 tag 之后的 changeSet。比如要回滚 drop5,通常是回滚到 drop4,而不是回滚到 drop5。
11.3 按 drop 阶段验证回滚
如果要验证某个 drop 阶段,例如 drop1,建议用空测试库按下面顺序执行:
text
update-to-tag-sql -> update-to-tag -> 查询 DATABASECHANGELOG 数量 -> rollback-count-sql -> rollback-count -> 验证表/视图是否删除
执行到 drop1:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-master.xml `
update-to-tag --tag=drop1
查询当前实际执行了多少个 changeSet:
sql
SELECT COUNT(*) AS executed_count
FROM DATABASECHANGELOG;
查看执行明细:
sql
SELECT ORDEREXECUTED, ID, AUTHOR, FILENAME, TAG, DATEEXECUTED
FROM DATABASECHANGELOG
ORDER BY ORDEREXECUTED;
如果只统计某个 drop 阶段,可以按文件名和 tag 过滤:
sql
SELECT COUNT(*) AS drop1_executed_count
FROM DATABASECHANGELOG
WHERE FILENAME LIKE '%drop1/%'
OR ID = 'master-tag-drop1';
回滚前先预览 SQL。这里的 <count> 要使用目标库 DATABASECHANGELOG 中实际查到的数量:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-master.xml `
rollback-count-sql --count=<count>
rollback-count-sql 只打印 SQL,不会真正修改数据库。日志中看到 DROP TABLE、DELETE FROM DATABASECHANGELOG 也只是预览结果。
确认预览 SQL 没问题后,再真正执行回滚:
powershell
docker run --rm `
-v "${PWD}\Db.Scripts\changelog:/liquibase/changelog" `
-w /liquibase/changelog `
-e LIQUIBASE_COMMAND_URL="jdbc:mariadb://<host>:<port>/<database>?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai" `
-e LIQUIBASE_COMMAND_USERNAME="<user>" `
-e LIQUIBASE_COMMAND_PASSWORD="<password>" `
-e LIQUIBASE_COMMAND_DRIVER="org.mariadb.jdbc.Driver" `
liquibasedbscript:<tag> `
--changelog-file=db.changelog-master.xml `
rollback-count --count=<count>
回滚后验证:
sql
SELECT COUNT(*) AS executed_count
FROM DATABASECHANGELOG;
sql
SELECT TABLE_NAME, TABLE_TYPE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME IN ('act_evt_log', 't_db_info', 'pf_message', 'T_PF_USER_SESSION');
注意:如果当前已经执行到 drop1 的 tag,rollback --tag=drop1 不会回滚 drop1 本身,因为它的语义是回滚到该 tag 所在位置。要回滚 drop1 本身,需要使用 rollback-count 回滚实际已经执行的 drop1 changeSet 数量。
12. 常见问题
12.1 file not found
报错:
text
The file drop2/xxx.xml was not found in the configured search path
原因通常是容器工作目录或 changelog 挂载路径不对。
处理:
- 设置
-w /liquibase/changelog。 - 挂载
Db.Scripts/changelog到/liquibase/changelog。 - 使用
--changelog-file=db.changelog-master.xml。
12.2 XML 属性不支持
报错:
text
Attribute 'ifExists' is not allowed to appear in element 'dropTable'
说明当前 Liquibase XML schema 不支持该属性。需要改成支持的写法,例如配合 preConditions 控制是否执行。
12.3 字段不存在
报错类似:
text
Unknown column '<column>' in 'field list'
处理思路:
- 查 DML 是否引用了该字段。
- 查 DDL/changelog 是否创建或新增过该字段。
- 如果 DML 来自高版本库导出,而当前目标库没有该字段,需要补 DDL 或调整 DML。
12.4 重复主键
报错:
text
Duplicate entry '<id>' for key 'PRIMARY'
处理思路:
- 查脚本内是否重复插入同一个主键。
- 查初始化 DML 是否已经插入过该数据。
- 查目标库是否已有该数据。
- 根据业务语义选择
DELETE + INSERT、REPLACE INTO、INSERT IGNORE或剔除重复数据。
13. 新增脚本流程
13.1 SQL 转 Liquibase XML
如果是结构变更,优先使用 Liquibase XML 标签表达,例如:
createTableaddColumndropColumncreateIndexaddUniqueConstraint
如果是大量 DML 或不适合结构化表达的 SQL,可以放入 <sql> 或 <sqlFile>。
13.2 添加到入口
全量初始化入口:
text
Db.Scripts/changelog/db.changelog-master.xml
现场增量入口:
text
Db.Scripts/changelog/db.changelog-incremental.xml
规则迁移入口:
text
Db.Scripts/changelog/db.changelog-rule-migration.xml
新增脚本时需要明确:
- 是否属于全量初始化。
- 是否属于现场增量。
- 是否属于独立数据迁移。
- 是否需要 rollback。
- 是否允许重复执行。
13.3 DDL 回滚策略
DDL 可以补 rollback 的,尽量补充。比如:
createTable对应dropTableaddColumn对应dropColumncreateIndex对应dropIndex
DML 一般不机械补 rollback,除非业务明确要求。
14. 推荐操作顺序
空库全量验证:
text
validate -> update-sql -> update -> 检查 DATABASECHANGELOG
已有库增量更新:
text
确认 Flyway 历史 -> 选择 changelog-sync 或 db.changelog-incremental.xml -> validate -> update-sql -> update
rule-migration:
text
确认基础表结构已存在 -> validate -> update-sql -> update -> 遇到失败修未成功的 changeSet -> 重新 update
异常处理:
text
先看失败的 changeSet ID -> 查 DATABASECHANGELOG 是否已记录 -> 判断是脚本问题、数据重复、字段缺失还是 checksum 问题 -> 再选择修复方式
15. 官方文档链接
常用入口:
- Liquibase 官方文档:https://docs.liquibase.com/
- Liquibase 命令参考:https://docs.liquibase.com/reference-guide/database-inspection-change-tracking-and-utility-commands
- Liquibase update 命令:https://docs.liquibase.com/reference-guide/init-update-and-rollback-commands/update
- Liquibase update-sql 命令:https://docs.liquibase.com/reference-guide/init-update-and-rollback-commands/update-sql
- Liquibase validate 命令:https://docs.liquibase.com/reference-guide/database-inspection-change-tracking-and-utility-commands/validate
- Liquibase status 命令:https://docs.liquibase.com/reference-guide/database-inspection-change-tracking-and-utility-commands/status
- Liquibase history 命令:https://docs.liquibase.com/reference-guide/database-inspection-change-tracking-and-utility-commands/history
- Liquibase clear-checksums 命令:https://docs.liquibase.com/reference-guide/database-inspection-change-tracking-and-utility-commands/clear-checksums
- Liquibase list-locks 命令:https://docs.liquibase.com/reference-guide/database-inspection-change-tracking-and-utility-commands/list-locks
- Liquibase release-locks 命令:https://docs.liquibase.com/reference-guide/database-inspection-change-tracking-and-utility-commands/release-locks
- Liquibase changelog-sync 命令:https://docs.liquibase.com/reference-guide/database-inspection-change-tracking-and-utility-commands/changelog-sync
- Liquibase rollback-count 命令:https://docs.liquibase.com/reference-guide/init-update-and-rollback-commands/rollback-count
- Liquibase rollback-count-sql 命令:https://docs.liquibase.com/reference-guide/init-update-and-rollback-commands/rollback-count-sql