Liquibase已执行变更集重复执行:删除表后原表重现
问题背景
我们使用Liquibase进行数据库变更部署,最初通过init_2023.yaml文件执行了FOOBAR表创建(FOOBAR-1)、唯一索引创建(FOOBAR-2)、唯一约束添加(FOOBAR-3)等变更集,执行正常。
为实现表独立版本管理,我们将init_2023.yaml拆分为按表命名的foobar.yaml,并新增删除FOOBAR表的变更集FOOBAR-4。执行该文件后表被正常删除,但重新导入时,Liquibase再次执行FOOBAR-1、FOOBAR-2、FOOBAR-3,导致已删除的表重现。
查看DATABASECHANGLOG表发现,这些变更集分别以init_2023.yaml和foobar.yaml为文件名记录了两次执行记录。我们预期同一ID的变更集不会重复执行,想确认是否与文件名有关,以及如何避免该问题。
相关变更集代码
原init_2023.yaml中的变更集(拆分后移入foobar.yaml)
databaseChangeLog: - changeSet: id: FOOBAR-1 failOnError: false author: anyA changes: - createTable: columns: - column: constraints: nullable: false name: COLA type: VARCHAR2(1000 BYTE) - column: name: COLB type: VARCHAR2(1000 BYTE) - column: name: VOLC type: VARCHAR2(1000 BYTE) - column: name: COLD type: VARCHAR2(1000 BYTE) tableName: FOOBAR - changeSet: id: FOOBAR-2 failOnError: false author: anyA changes: - createIndex: columns: - column: name: COLA indexName: FOOBAR_PK tableName: FOOBAR unique: true - changeSet: id: FOOBAR-3 failOnError: false author: anyA changes: - addUniqueConstraint: columnNames: COLA constraintName: FOOBAR_PK forIndexName: FOOBAR_PK tableName: FOOBAR
foobar.yaml新增的变更集
- changeSet: id: FOOBAR-4 failOnError: false author: anyB changes: - dropTable: cascadeConstraints: true tableName: FOOBAR
DATABASECHANGLOG表记录
| ID | AUTHOR | FILENAME | DATEEXECUTED | ORDEREXECUTED | MD5SUM | DEPLOYMENT_ID | -------- | ------ | -------------- | ------------------- | ------------- | ------------------- | | FOOBAR-3 | anyA | foobar.yaml | 23.10.2023 12:00:00 | 517 | 8:foobar3md5sum_abc | 63 | FOOBAR-2 | anyA | foobar.yaml | 23.10.2023 12:00:00 | 516 | 8:foobar2md5sum_def | 63 | FOOBAR-1 | anyA | foobar.yaml | 23.10.2023 12:00:00 | 515 | 8:foobar1md5sum_ghi | 63 | FOOBAR-4 | anyB | foobar.yaml | 23.10.2023 11:00:00 | 504 | 8:foobar2md5sum_def | 52 | FOOBAR-3 | anyA | init_2023.yaml | 01.09.2023 12:00:00 | 103 | 8:foobar3md5sum_abc | 11 | FOOBAR-2 | anyA | init_2023.yaml | 01.09.2023 12:00:00 | 102 | 8:foobar2md5sum_def | 11 | FOOBAR-1 | anyA | init_2023.yaml | 01.09.2023 12:00:00 | 101 | 8:foobar1md5sum_ghi | 11
原因分析
Liquibase判断变更集是否已执行的唯一标识是ID + AUTHOR + FILENAME的组合,而非仅依赖ID。当你把变更集从init_2023.yaml迁移到foobar.yaml后,FILENAME字段发生变化,Liquibase会将这些变更集视为全新的未执行项,因此会重复执行。
解决方案
方案1:用addchangeloglock标记历史执行记录
在foobar.yaml中添加变更集,将原init_2023.yaml中的FOOBAR-1/2/3标记为已执行,避免Liquibase重复运行:
- changeSet: id: FOOBAR-mark-history-executed author: anyA changes: - addchangeloglock: id: FOOBAR-1 author: anyA filename: init_2023.yaml - addchangeloglock: id: FOOBAR-2 author: anyA filename: init_2023.yaml - addchangeloglock: id: FOOBAR-3 author: anyA filename: init_2023.yaml
执行该变更集后,Liquibase会更新DATABASECHANGLOG的执行状态,后续运行foobar.yaml时会自动跳过FOOBAR-1/2/3。
方案2:手动修改DATABASECHANGLOG记录(不推荐)
直接更新数据库中的DATABASECHANGLOG表,将原记录的FILENAME从init_2023.yaml改为foobar.yaml。此方法需直接操作数据库,风险较高,仅适合测试环境或紧急场景。
方案3:重新设计变更集ID(长期推荐)
后续拆分变更文件时,为变更集分配唯一ID(例如在原ID后追加文件标识,如FOOBAR-1-foobar),避免同一ID出现在不同文件中。这能从根源上规避此类问题,更符合Liquibase的设计规范。
内容的提问来源于stack exchange,提问作者MKochenburger

