checksum mismatch 不是 Flyway 抽風,是它的一種保護:你改了已經應用過的遷移腳本,本地重算的 checksum 和數據庫裡存的對不上。別急著
repair,先想清楚該不該改這個腳本——長期正確做法是新增遷移,而不是原地改歷史。
Issue & Env(案發現場與環境)
- 現象:應用啟動到 Flyway 校驗階段失敗,進程退出,數據庫連接其實已經建立好了。
- 環境:Spring Boot 2.x / 3.x,Flyway(
flyway-core+spring-boot-starter-jdbc),遷移腳本預設放在src/main/resources/db/migration。
StackTrace(堆疊日誌)
啟動日誌裡的校驗失敗:
org.flywaydb.core.api.FlywayException: Validate failed: Migrations have failed validation
Migration checksum mismatch for migration version 1.0
-> Applied to database : 87747630
-> Resolved locally : 1882320251
at org.flywaydb.core.Flyway.doValidate(Flyway.java:325)
at org.flywaydb.core.Flyway.migrate(Flyway.java:187)
at org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer.afterPropertiesSet(FlywayMigrationInitializer.java:36)
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178)
at org.springframework.context.support.DefaultLifecycleProcessor.lambda$startBeans$1(DefaultLifecycleProcessor.java:152)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1767)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:750)
兩行數字是關鍵:Applied to database 是數據庫記錄的歷史 checksum,Resolved locally 是它現在根據本地腳本重算出來的值,兩者不一致就報錯。
Reproduce(復現程式碼)
數據庫已經應用過 V1__create_user.sql,之後你手賤改了它——哪怕只加一行註釋:
-- V1__create_user.sql(改前)
create table user_info (
id bigint primary key
);
-- V1__create_user.sql(改后,加了一行注释)
-- add comment
create table user_info (
id bigint primary key
);
再次啟動應用,Flyway 校驗階段就會拋 checksum mismatch。
Root Cause(根因分析)
Flyway 第一次應用某個遷移腳本時,把腳本內容算出一個 checksum,連同版本號、描述一起寫進 flyway_schema_history 表。之後每次啟動,Flyway 在 validate 階段會重新讀本地腳本、重算 checksum,再和表裡的歷史值比對。
只要你改了已經應用過的腳本,checksum 就變,比對自然失敗。這個機制是刻意的:它防止你把自己都不知道的歷史改動帶進各個環境,尤其是生產。常見的觸發原因有幾類:
- 直接編輯了已應用的
V*腳本,哪怕只改註釋、重新格式化。 - 文件換行符漂移(Windows 的 CRLF 和 Unix 的 LF 混用),內容一樣 checksum 卻不同。
- 文件編碼不一致,腳本里含中文等非 ASCII 字符時尤其明顯。
The Fix(解決方案)
分場景處理,別一上來就 repair。
先判斷:這個腳本現在還該不該是原來的樣子。
首選——腳本不該改。 已應用的 versioned migration 就當歷史資產凍結,新變更寫成新腳本:
-- V2__add_email_column.sql
alter table user_info add column email varchar(255);
腳本被誤改。 把內容改回應用時的原樣,checksum 就恢復,不用碰數據庫。
確認新內容才是對的、且庫結構和腳本一致。 這時才用 repair 重算並更新歷史記錄:
mvn flyway:repair \
-Dflyway.url=jdbc:mysql://localhost:3306/your_db \
-Dflyway.user=root \
-Dflyway.password=xxx
repair 只是把 flyway_schema_history 裡的 checksum 刷新成當前腳本的值,不改變你的表結構。生產環境用它要格外謹慎,得先確認庫裡的實際結構確實和腳本一致。
開發環境最省事。 直接重建庫,讓 Flyway 從零跑一遍:
DROP DATABASE IF EXISTS your_db;
CREATE DATABASE your_db;
編碼問題從根上防:腳本統一存成 UTF-8,並在設定裡顯式聲明:
spring.flyway.encoding=UTF-8
這樣能避免不同編輯器、不同操作系統之間的編碼漂移,從源頭少掉一類 checksum 變更。
