1. 概述
Flyway是獨立於數據庫的應用、管理並跟蹤數據庫變更的數據庫版本管理工具。
自動升級(自動發現更新項):Flyway 會將任意版本的數據庫升級到最新版本。Flyway 可以脫離JVM 環境通過命令行執行,可以通過Ant 腳本執行,通過Maven 腳本執行(這樣就可以在集成環境自動執行),並且可以在應用中執行(比如在應用啓動時執行)。
規約優於配置:Flyway 有一套默認的規約,所以不需要修改任何配置就可以正常使用。
既支持SQL 腳本,又支持Java 代碼:可以使用SQL 腳本執行數據庫更新,也可以使用Java 代碼來進行一些高級數據升級操作。
高可靠性:在集羣環境下進行數據庫升級是安全可靠的。
支持清除已存在的庫表結構:Flyway 可以清除已存在的庫表結構,可以從零開始搭建您的庫表結構,並管理您的數據庫版本升級工作。
支持失敗修復。新的2.0 版本提供了repair 功能,用於解決數據庫更新操作失敗問題。
2. flyway基礎
2.1概述
- flyway通過在數據庫中維護一張版本記錄表(默認名稱爲flyway_schema_history),對數據庫腳本操作進行版本管理,使得數據庫的升級更新可以自動化;
- flyway執行的腳本支持兩種形式:一是使用SQL編寫的.sql腳本,二是把SQL操作寫到java代碼裏,通過執行java代碼來執行SQL;
- 每個.sql或.class文件都是一個版本,文件名稱需要遵循flyway約定的格式;這個名稱中包含了一個數字的版本信息,flyway通過這個版本來進行管理;
2.2 flyway組成
- flyway包由三部分組成
- commandline: 對輸入的命令進行處理,確定配置文件路徑,查找依賴包等;
- core: 實現flyway核心的操作(migrate/info/clean等)和版本管理
- maven-plugin和gradle-plugin: 用於幫助打可運行的jar包;
- flyway由java語言編寫,可以直接使用core來進行開發,而不需要使用commandline
- 比如:自己建個springboot啓動工程(web或application),在裏面實例化Flyway對象,然後執行對應的操作
- 比如:自己寫個main方法,在裏面實例化Flyway對象,然後執行對應的操作
2.3 flyway操作
命令 | 說明 |
---|---|
migrate | 執行最新版本的SQL腳本和java代碼,把結果反映到數據庫上 |
clean | 清除數據庫中的所有表與數據,只剩下一個空的數據庫 |
info | 列出用於維護版本信息的表內容,表名默認爲flyway_schema_history |
validate | 用實際的版本與數據庫中已有的版本進行對比,檢測意外修改(文件有checksum) |
baseline | 設定基線版本,對於已存在數據的數據,必須設置基線版本,後面從該版本之後的版本進行操作 |
repair | 把版本表中失敗的記錄狀態變成pending,然後可以重新操作 |
undo | 回滾對應版本,只有商用版本才支持 |
2.4 版本
- Java文件
- SQL文件
- SQL文件與Java文件的不同之處僅在於後綴不同,一個是.sql(可配置),另外一個是.class(不可配置),其它是相同的;
- 文件名(不含後綴)的格式
- Prefix: 前綴,可以通過配置文件來設置
- V: 代表正常版本管理;
- U: 表示回滾,該操作比較危險,需要做詳盡測試才能用,實踐中最好不用;
- R: 表示可以重複執行的版本,比如建視圖、存儲過程、函數等;
- Version: 版本號,必須由數字(可加下劃線或點)組成,重複執行的腳本不需要版本;
- Separator: 分隔版本與description的分隔符,爲兩個下劃線,可通過配置文件來修改,默認爲兩個下劃線;
- Description: 文件內容描述,建議用下劃線分隔單詞;
- Suffix: 只針對於sql文件(java文件不適用),可通過配置文件來設置,默認爲.sql;
- Prefix: 前綴,可以通過配置文件來設置
- Version
- 一般情況下使用1~N的數字即可;
- 考慮到實際開發中可能有多個分支進行操作,爲了使得分支合併不衝突,可使用時間戳來做版本(中間可用下劃線或點來分隔),時間戳越精確、衝突可能性越小;
2.5 路徑
- INSTALLDIR: 安裝路徑,即flyway解壓所在的路徑,其用於:
- 加載${INSTALLDIR}/drivers下的各種數據庫driver對應的jar包;
- 加載${INSTALLDIR}/conf下的全局配置文件;
- workingDirectory: 工作目錄,可通過指定命令參數“-workingDirectory=xxxx”(不含雙引號,xxx換成具體的絕對路徑),如果不指定則等同INSTALLDIR;
- 加載${workingDirectory}/jars裏的jar包,通過java編寫的代碼需要打成jar放到該目錄下才能通過命令行的方式訪問到;
- 加載${workingDirectory}/sql裏的SQL腳本文件,這個目錄會被配置文件flyway.conf裏的flyway.locations的值替換掉(注意是替換掉,不是並集,這個地方不好理解,原理是這兩個目錄都是賦值給location變量,配置文件裏有設置時會覆蓋掉);
- 在flyway.conf裏的flyway.locations值,如果以filesystem:開頭、且是相對路徑的,都會基於此路徑;
- 注意:該部分在flyway官網文檔中無說明(需要查看源碼才能知曉),也不好理解,容易出錯,需要好好理解(有比較多的地方並不遵循後面覆蓋前面的邏輯,所以不好理解)。這些路徑是在commandline中實現的,也就是說只有使用commandline纔用到
2.6 配置
2.6.1 配置加載
配置主要來源於flyway.conf配置文件,按以下順序和路徑進行加載配置:
- 先加載${INSTALLDIR}/conf/flyway.conf(flyway.conf文件名不可更改);
- 加載System.property中配置user.home對應的路徑下的flyway.conf文件(flyway.conf文件名不可更改),加載的配置若有相同則會覆蓋之前加載的;
- System properties可以通過下面方式增加
- java啓動時使用-D加的參數,如java -Dxxx=xxxx;
- 在java代碼中使用System.setProperty(“xxx”, “xxxx”)添加的參數;
- System properties可以通過下面方式增加
- 加載當前目錄下的flyway.conf文件(flyway.conf文件名不可更改),加載的配置若有相同則會覆蓋之前加載的;
- 在命令行中,terminal或cmd進入的路徑爲當前路徑
- 在java工程中運行則是工程根目錄
- 該操作有點隱蔽,注意理解;
- 若在環境變量中配置了flyway.configFiles,會加載這些文件,加載的配置若有相同則會覆蓋之前加載的;
- 該配置值可以有多個文件,用逗號分隔,每個文件可以有相對路徑(不能使用絕對路徑),文件名也不一定是flyway.conf;
- 這些配置文件會基於{workingDirectory}目錄下;
- 若命令行參數中帶-configFiles參數指定了配置文件,則會加載這些文件,加載的配置若有相同則會覆蓋之前加載的;
- 若環境變量中配置了flyway.configFiles,則此配置參數無效;
- 該配置值可以有多個文件,用逗號分隔,每個文件可以有相對路徑(不能使用絕對路徑),文件名也不一定是flyway.conf;
- 這些配置文件會基於{workingDirectory}目錄下;
- System.in輸入的配置(貌似用於測試,待研究);
- 用逗號分隔flyway.locations的值,如果裏面的路徑不是絕對路徑,則用${workingDirectory}補齊;
- 命令行還可以指定-flyway.xxxx=xxx形式的配置,這些配置會覆蓋上面已載的配置項
- 注意:此處配置項,不是加載配置文件;
注意:
- 該部分在flyway官網文檔中無說明(需要查看源碼才能知曉),也不好理解,因爲配置文件名大多是固定不可變的,而可以改變的手段有點特殊。
- 要改變flyway.conf配置文件名稱,只有兩種方式;
- 該部分主要屬於commandline範疇,直接使用core則可以自行構造配置項或自行指定配置文件並加載;
- 配置裏的路徑可以使用變量(原理待研究);
- 在命令行中指定-workingDirectory=xxxx時,若也指定-configFiles=flyway.conf,則會比較明顯地知道配置文件是在workingDirectory目錄下,但如果不指定configFiles則需要flyway.conf放到命令行進入的目錄中,會有點隱蔽;
- 配置文件裏參數讀取後,{}整體被替換爲空(來源於命令行的參數沒有此處理過程,故不能使用${}格式的變量);
2.6.2 常用配置
# 配置sql和java code查找路徑:
# sql文件路徑必須以filesystem:(含冒號)開頭,可以使用變量${workdir};
# java code路徑需要以classpath:(含冒號)開頭,java代碼需要打成jar包,並放到工作目錄裏的jars目錄下,jar裏的目錄與該配置中classpath:後配置的路徑一致;
# 開發環境不要修改此值,它會自動找工程目錄下的db/sql中的sql腳本,以及代碼目錄com/everhomes/dbmigrate中的java代碼(兩種都會遞歸找子目錄)
flyway.locations=filesystem:${workdir}/sql,classpath:com/everhomes/dbmigrate
# 配置數據庫連接,注意在url中不要配置用戶名和密碼
flyway.url=jdbc:mysql://127.0.0.1:13306/blogs?characterEncoding=UTF-8&serverTimezone=UTC
flyway.user=root
flyway.password=123456
# 當需要低版本的腳本時,需要置成true,否則只能執行高版本的腳本
flyway.outOfOrder=true
# 當數據庫不是空數據庫時,需要配置爲true,空數據庫則可以不配置
# flyway.baselineOnMigrate=true
# 配置了該版本後,migrate會從比該版本大的版本進行migrate,不配置時默認值爲1
# flyway.baselineVersion=1
# 有數據的數據庫請保留此值爲true,禁用clean命令,執行clean命令會清空數據庫
flyway.cleanDisabled=true
# 記錄migrate歷史記錄的表名,默認爲 flyway_schema_history
flyway.table=schema_version_history
# migrate前後需要做某些操作時可使用此配置項,值爲類名(帶包路徑),多個類名之間使用逗號分隔
# 每個類需要實現org.flywaydb.core.api.callback.Callback接口
# flyway.callbacks=com.everhomes.dbmigrate.core.MigrateCallBack
2.7 版本管理
支持SQL-base和Java-base兩種管理方式,兩種方式產生的版本文件是共同管理的,即在命令行或Java代碼工程裏都有可以同時執行.sql和.class提供的版本管理,執行順序按版本號順序;
2.7.1 SQL-base migrate
基於SQL的版本管理:
- 文件名稱需要符合版本格式
- 文件可以被找到
- SQL文件是通過flyway.locations配置項中指定以filesystem:開頭的路徑,可以有多個、用逗號分隔;
- flyway.locations可以通過配置文件或命令行參數指定(參考上面“配置加載”章節說明);
- 注意:flyway.locations值雖然可以通過多個途徑配置,但只能配置一個值(值中可逗號分隔),注意加載的順序和覆蓋情況;
- 確定上面兩點後,即可執行flyway migrate/info/clean/repair 來進行版本管理;
- 命令參數中可以按規範指定參數來幫助找到flyway.locations
2.7.2 Java-base migrate
基於Java的版本管理:
- jar形式
- 把開發的java代碼打包jar包,放到${workingDirectory}/jars中
- java代碼須繼承org.flywaydb.core.api.migration.BaseJavaMigration(推薦)或實現org.flywaydb.core.api.migration.JavaMigration接口,使用此方式僅依賴flyway和jdk提供的jar即可,配置環境簡單;
- java代碼儘量使用flyway和jdk提供的類和自定義的類來進行操作,否則要解決包依賴問題;
- java代碼路徑可被找到(在jar包裏的路徑)
- java代碼如果是放到db/migration路徑下(可有子目錄),則不用配置其它路徑即可訪問,因爲flyway裏默認給location加了個目錄: classpath:db/migration
- 如果不是放到db/migration路徑下,則需要在flyway.locations裏配置以classpath:開頭的路徑,該路徑要與jar包裏的路徑一致;
- 把開發的java代碼打包jar包,放到${workingDirectory}/jars中
- java工程形式(在IDE裏運行java工程的形式)
- 工程啓動運行有兩種方式
- 引flyway的commandline包
- 可指定flyway的安裝目錄,或者自行依賴數據庫driver包和flyway的core包;
- 指定workingDirectory目錄;
- 指定flyway.conf的目錄,或按規則放到commandline可找到的目錄;
- 運行後執行commandline提供的org.flywaydb.commandline.Main.main(args)
- 優點:可利用commandline已經實現的命令操作,按命令的方式操作;
- 缺點:需要遵循commandline的方式;
- 實例化flyway並運行
- 按springboot的方式啓動或自行寫main方法啓動
- 把flyway的core引入到工程中
- 實例化flyway,並指定數據庫連接,然後調用migrate()/clean()等方法
- 優點:靈活;
- 缺點:需要對flyway各命令所依賴的配置項有比較多瞭解,纔好操作;
- 引flyway的commandline包
- 該方式不受限於db/migration目錄,可隨意定目錄,但爲了部署時可以打包jar包,推薦遵循jar包所要求的目錄方式;
- 工程啓動運行有兩種方式
- 注意:
- 該部署在官方文檔說明不詳細,比較模糊,特別是jar的方式就沒有說明,部署時也只能使用jar方式;
- 官方文檔主要說明了“實例化flyway”方式,但其背後依賴的配置項和規則也沒有說明;
2.7.3 callback
- 版本管理操作有生命週期管理:整個操作開始前->單個版本操作開始前->單個statement操作開始前-> 具體操作 -> 單個statement操作後(報錯後)-> 單個版本操作後(錯誤後)-> 整個操作後(錯誤後)
- 整個操作可能包含多個版本,每個版本里可能有多個sql statement
- 操作是指migrate、clean、repair等,參考上面操作說明
- 這些生命週期節點可以設置callback來進行一些額外的處理
- 在java代碼中,需要實現org.flywaydb.core.api.callback.Callback接口,類路徑無規定
- 接口要實現supports()方法,在裏面指定哪些操作歸該Callback進行處理
- 實現handle()方法,具體處理業務
- canHandleInTransaction()在有事務時才涉及;
- 在flyway.callbacks配置中指定callback的類路徑(帶包名,多個類之間使用逗號分隔),flyway.callbacks可以在flyway.conf中指定或者在命令行參數中指定(參考上面配置加載章節說明);
- callback的類包需要包含在jar中,才能在命令行裏操作;
- 在java代碼中,需要實現org.flywaydb.core.api.callback.Callback接口,類路徑無規定
2.7.4 不支持MySQL5.6
- 在org.flywaydb.core.internal.database.mysql.MySQLDatabase.ensureSupported()中有對MySQL數據庫的版本進行判斷,小於5.7的版本不支持(MARIADB是MySQL的一個分支,都在MySQLDatabase中實現)。
- 除此之外,沒有其它地方控制這個版本。拋出的錯誤說enterprise版本支持,但除非換代碼,否則感覺也不支持。
- 要支持5.6,可以修改此類編譯與.class,然後替換掉core包裏的該類。
public final void ensureSupported() {
ensureDatabaseIsRecentEnough("5.1");
if (databaseType == DatabaseType.MARIADB) {
ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition("10.1", org.flywaydb.core.internal.license.Edition.ENTERPRISE);
ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition("10.2", org.flywaydb.core.internal.license.Edition.PRO);
recommendFlywayUpgradeIfNecessary("10.4");
} else {
ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition("5.7", org.flywaydb.core.internal.license.Edition.ENTERPRISE);
if (JdbcUtils.getDriverName(jdbcMetaData).contains("MariaDB")) {
LOG.warn("You are connected to a MySQL " + getVersion() + " database using the MariaDB driver." +
" This is known to cause issues." +
" An upgrade to Oracle's MySQL JDBC driver is highly recommended.");
}
recommendFlywayUpgradeIfNecessary("8.0");
}
}
protected final void ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition(String oldestSupportedVersionInThisEdition, Edition editionWhereStillSupported) {
if (!getVersion().isAtLeast(oldestSupportedVersionInThisEdition)) {
throw new FlywayEditionUpgradeRequiredException(
editionWhereStillSupported,
databaseType,
computeVersionDisplayName(getVersion()));
}
}
2.7.5 版本比較
- flyway把版本都封裝在org.flywaydb.core.api.MigrationVersion類裏
- 比較的原理
- 先把版本中的下劃線(_)替換成點(.),不支持其它非數字字符,有的話會報錯;
- 把版本以點進行分隔成多個數字(點分隔的每段一個數字);
- 把每一段數字轉成Bigdecimal,即版本的每段數字都可以很大,即使精確到毫秒的時間戳作爲版本也支持;
- 兩個版本之間進行比較
- 取兩個版本中段數量大的段數作爲循環次數;
- 循環比較,從左到右,每一段相比(Bidecimal相比),某段大的則版本大,若段數不夠則用0代替;
- 注意
- 每段都是數字值相比,不是按字符串相比
- 段數的多或少並不能說明版本的大小,比如並不是段數多的版本大;
- 段是從左到右比的,哪段先大就版本大;
2.8 日誌
- flyway本身提供了日誌的方式,但實現也比較混亂
- 比如可以通過命令行參數指定-outputFile=xxx或-logFile=xxx來輸出日誌文件,通過指定-X或-q來分別控制日誌的級別爲DEBUG或WARN,但這些參數生效的前提是在環境中找不到slf4j等常用的日誌方式纔會生效
- 這些日誌的格式無法控制,打印出來的日誌時間沒有日期
- 使用logback
- 依賴slf4j相關的三個包:logback-classic-1.2.3.jar、logback-core-1.2.3.jar、slf4j-api-1.7.25.jar
- java代碼工程環境直接依賴即可
- 命令行環境則需要把這些包放到${INSTALLDIR}/lib,可能也可以用其它方式依賴(待研究);
- logback.xml文件路徑
- 在java代碼工程環境中,如果是使用springboot環境,則直接放到classpath中即可找到(springboot有自己找logback的方式),否則依賴slf4j的查找方式(待研究);
- 在命令行環境中,可在java命令啓動的時候加入-Dlogback.configurationFile=xxx/logback.xml 參數來指定logback.xml文件(不確定是否有更優雅的方式,待研究)
- 依賴slf4j相關的三個包:logback-classic-1.2.3.jar、logback-core-1.2.3.jar、slf4j-api-1.7.25.jar